Проблема не в Vim, а в неверной ментальной модели. В большинстве редакторов вкладка = открытый файл, и точка. В Vim это три разные, ортогональные сущности: буфер, окно и вкладка. Понять, что это не синонимы, - главное озарение этой главы. Когда модель встанет на место, вы перестанете "терять файлы" и начнёте собирать рабочее пространство под конкретную задачу за пару нажатий.
Три сущности: buffer != window != tab
Запомните это определение дословно, оно лежит в основе всего:
- Буфер (buffer) - это файл, загруженный в память. Текст и его состояние. Буфер существует независимо от того, видите вы его на экране или нет.
- Окно (window) - это вьюпорт, прямоугольная область, через которую вы смотрите на буфер. Окно - это "вид на буфер".
- Вкладка (tab page) - это коллекция окон, то есть целый раскладочный лэйаут. Вкладка в Vim - НЕ файл, а рабочий стол с расставленными на нём окнами.
Из этой модели сразу следуют важные выводы:
- Один буфер может быть показан в нескольких окнах одновременно (правки видны синхронно - это один и тот же текст).
- Закрытие окна не выгружает буфер из памяти.
- Вкладка в Vim - это про раскладку окон, а не про "ещё один открытый файл", как в VS Code. Если вы используете вкладки как список файлов, вы используете их не по назначению (об этом ниже).
Что такое буфер на самом деле
Каждый раз, когда вы открываете файл, Vim создаёт буфер. У буфера есть номер (стабильный в рамках сессии), имя (обычно путь к файлу) и флаги состояния. Даже когда файл "ушёл с экрана", его буфер обычно остаётся загруженным - поэтому вернуться к нему мгновенно.
Открыть файл в новом буфере - :edit (сокращённо :e):
Код: Выделить всё
:e src/main.lua " открыть файл в текущем окне (создаётся/активируется буфер)
:e! " перечитать текущий файл с диска, отбросив несохранённые правки
Чтобы увидеть всё, что загружено в память, - :ls (он же :buffers, он же :files):
Код: Выделить всё
:ls
1 #h "src/main.lua" line 42
2 %a "src/utils.lua" line 1
3 h "README.md" line 10
4 a + "init.lua" line 88
Код: Выделить всё
Флаг | Значение
------+-----------------------------------------------------
% | буфер в текущем окне
# | альтернативный буфер (туда прыгает Ctrl-^)
a | active - загружен и показан в каком-то окне
h | hidden - загружен, но не показан (живёт в памяти)
+ | модифицирован, есть несохранённые изменения
- | буфер с выключенной 'modifiable' (set nomodifiable)
= | буфер только для чтения (readonly)
Переключение между буферами
Код: Выделить всё
Команда | Действие
------------------------+------------------------------------------------------------------------------------------
:b {N} | перейти к буферу с номером N
:b {имя} | перейти к буферу по части имени (с <Tab>-дополнением)
:bn / :bnext | следующий буфер
:bp / :bprevious | предыдущий буфер
:bf / :bfirst | первый буфер
:bl / :blast | последний буфер
:sb {буфер} / :sbuffer | открыть буфер в горизонтальном сплите (а не в текущем окне)
:vert sb {буфер} | то же, но в вертикальном сплите
:sball | открыть все буферы из :ls, по окну на каждый (:vert sball - вертикально)
Ctrl-^ (он же Ctrl-6) | мгновенно прыгнуть на альтернативный буфер (#) - обычно тот, откуда вы только что пришли
:b принимает не только номер, но и подстроку имени: :b util<Tab> дополнит до utils.lua. Это часто быстрее, чем помнить номера.
Практический поток "было -> нажимаем -> стало":
Код: Выделить всё
" Открыто 5 файлов, нужно из main.lua прыгнуть в config
:b conf<Tab><CR> " Vim дополняет до config.lua и переключает буфер
" мгновенно вернуться туда, откуда пришли:
Ctrl-^
Код: Выделить всё
:bd " buffer delete - выгрузить текущий буфер из списка
:bd 3 " выгрузить буфер №3
:bd main.lua " выгрузить по имени
:bd! " принудительно, отбросив несохранённые правки
Окна-сплиты: несколько видов одновременноПолезная опция: set hidden (в Vim 9.1 по умолчанию ВЫКЛючена, в Neovim - ВКЛючена). Она разрешает уходить из модифицированного буфера, не сохраняя его, - буфер просто становится hidden вместо требования "сохрани или потеряешь". Без неё :bn на несохранённом файле выдаст E37: No write since last change. Нюанс: если буфер не модифицирован, :bn и подобные переключатели работают и без hidden - ошибка возникает именно при попытке уйти с несохранённых правок.
Окна нужны, когда надо видеть несколько мест сразу: реализацию рядом с тестом, два конца длинного файла, diff. Помните: окно - это вид на буфер, и одно и то же может быть открыто в нескольких окнах.
Создание сплитов
Код: Выделить всё
Команда | Действие
---------------+---------------------------------------------------
:sp / :split | горизонтальный сплит (окно сверху) того же буфера
:sp {файл} | горизонтальный сплит с открытием файла
:vs / :vsplit | вертикальный сплит (окно слева)
:vs {файл} | вертикальный сплит с открытием файла
Ctrl-W s | то же, что :sp (горизонтальный)
Ctrl-W v | то же, что :vs (вертикальный)
Ctrl-W n | новое горизонтальное окно с пустым буфером
Навигация между окнами
Все оконные команды начинаются с префикса Ctrl-W. Движение между окнами повторяет логику hjkl:
Код: Выделить всё
Сочетание | Действие
---------------------+--------------------------------------------
Ctrl-W h | в окно слева
Ctrl-W j | в окно снизу
Ctrl-W k | в окно сверху
Ctrl-W l | в окно справа
Ctrl-W w | в следующее окно по кругу
Ctrl-W p | в предыдущее (последнее посещённое) окно
Ctrl-W t / Ctrl-W b | в самое верхнее-левое / нижнее-правое окно
Размеры окон
Код: Выделить всё
Сочетание | Действие
----------------------------+-----------------------------------------------
Ctrl-W + | увеличить высоту текущего окна на 1 строку
Ctrl-W - | уменьшить высоту на 1 строку
Ctrl-W > | расширить (ширина)
Ctrl-W < | сузить (ширина)
Ctrl-W = | уравнять размеры всех окон
Ctrl-W _ | развернуть текущее окно по высоте на максимум
<code>Ctrl-W |</code> | развернуть по ширине на максимум
10 Ctrl-W + | увеличить на 10 строк (с count)
Перестановка и закрытие окон
Код: Выделить всё
Сочетание | Действие
----------------+------------------------------------------------------------------------------
Ctrl-W r | прокрутить (rotate) окна по кругу
Ctrl-W x | поменять местами (exchange) текущее окно со следующим
Ctrl-W H/J/K/L | переместить текущее окно к левому/нижнему/верхнему/правому краю (заглавные!)
Ctrl-W o | only - закрыть все окна, кроме текущего
Ctrl-W c | close - закрыть текущее окно (буфер остаётся)
Ctrl-W q | quit - выйти из окна (как :q)
:q | закрыть текущее окно
Заглавные Ctrl-W H/J/K/L превращают, например, горизонтальный сплит в вертикальный: встали в нижнее окно, нажали Ctrl-W L - и оно уехало в правую колонку.
Практический пример "было -> нажимаем -> стало":
Код: Выделить всё
" Один файл на экране. Хотим видеть начало и конец рядом, вертикально.
:vs " теперь два окна с тем же буфером, бок о бок
Ctrl-W l " переходим в правое окно
G " в правом - в конец файла; в левом видно начало
" наигрались, сворачиваем обратно:
Ctrl-W o
Здесь приходится ломать привычку, принесённую из других редакторов. В VS Code, браузере и IDE вкладка = открытый файл. В Vim вкладка - это раскладка окон (лэйаут), а не файл.
Команды вкладок
Код: Выделить всё
Команда | Действие
--------------------------+---------------------------------------------------------
:tabnew / :tabe {файл} | новая вкладка (опционально с файлом)
:tabclose / :tabc | закрыть текущую вкладку
:tabonly / :tabo | закрыть все вкладки, кроме текущей
gt / :tabn | следующая вкладка
gT / :tabp | предыдущая вкладка
{N}gt | перейти к вкладке номер N (нумерация с 1: 1gt - первая)
:tabnext {N} / :tabn {N} | то же - к вкладке N, тоже с 1
:tabs | список вкладок и окон в них
:tab split | продублировать текущее окно в новой вкладке
Когда вкладки уместны, а когда нетВажно про нумерацию: и {N}gt, и :tabnext {N} нумеруют вкладки с единицы, а не с нуля. Первая вкладка - 1gt, вторая - 2gt. Новички по привычке из массивов ищут "нулевую вкладку" - её нет. При этом gt без числа - это просто "следующая вкладка", а не "вкладка 0".
Антипаттерн: использовать :tabnew как "открыть ещё один файл" и листать gt/gT как список открытых файлов. Это борьба с инструментом. Список открытых файлов в Vim - это буферы (:ls, :b), и их может быть сотня без всякого визуального шума.
Вкладки же оправданы, когда вам нужны разные раскладки окон под разные задачи. Например:
- Вкладка 1: код рядом с тестом (два вертикальных сплита).
- Вкладка 2: файловый менеджер слева + большой буфер справа.
- Вкладка 3: git-diff или quickfix-список на весь экран.
Код: Выделить всё
" Соберём отдельный контекст под рефакторинг:
:tabnew " чистая вкладка
:e src/service.lua " открыли сервис
:vs src/service_spec.lua " тест рядом
" прыжок назад к основной вкладке:
gT
" и обратно:
gt
Список аргументов (arglist) - это отдельный от буферов, упорядоченный набор файлов, с которым удобно работать как с "текущей пачкой на обработку". Это те файлы, что вы передали Vim в командной строке (vim *.lua), но его можно переопределять на лету.
Код: Выделить всё
Команда | Действие
----------------+-------------------------------------------------
:args | показать текущий список аргументов
:args {шаблон} | задать новый список (например :args src//*.lua)
:argadd {файл} | добавить файл в список
:argdelete | удалить из списка
:next / :n | следующий файл в arglist
:prev / :N | предыдущий файл в arglist
:first / :last | к первому / последнему
Код: Выделить всё
:args src/**/*.lua " отобрали все Lua-файлы под src/ как рабочую пачку
:args " проверили список (текущий помечен [квадратными скобками])
Пакетные команды: :bufdo, :argdo, :windo, :tabdoВ Neovim шаблон ** для рекурсии работает из коробки; в Vim полезна опция set path и ** тоже поддерживается в :args.
Вот ради чего вся эта инфраструктура и существует: выполнить одну Ex-команду над множеством буферов, файлов, окон или вкладок. Каждая команда соответствует своей сущности.
Код: Выделить всё
Команда | Над чем работает
--------------+--------------------------------------
:bufdo {cmd} | над каждым буфером из :ls
:argdo {cmd} | над каждым файлом из arglist (:args)
:windo {cmd} | над каждым окном текущей вкладки
:tabdo {cmd} | над первым окном каждой вкладки
Код: Выделить всё
:args src/**/*.lua
:argdo %s/oldName/newName/ge | update
- %s/.../.../g - замена по всему файлу.
- Флаг e подавляет ошибку "Pattern not found" - без него :argdo упадёт на первом же файле, где паттерна нет.
- | update - записать файл на диск, только если он изменился (update экономнее, чем write).
Здесь же кроется вторая ловушка. :bufdo/:argdo неявно подразумевают включённую 'hidden': при переходе к следующему буферу Vim пытается покинуть текущий, и если в нём остались несохранённые правки, а hidden выключена, команда оборвётся на E37: No write since last change. Поэтому либо включите set hidden, либо записывайте каждый буфер прямо в команде (| update как выше), либо явно глушите проверку через set eventignore / флаг ++ - но самый чистый способ это именно | update, который и сохраняет результат, и снимает требование hidden.
:bufdo против :argdo
:bufdo пройдёт по всем загруженным буферам - включая случайно открытые, справку, файловые менеджеры. :argdo - только по тому, что вы сознательно положили в arglist. Для рискованных пакетных правок предпочитайте :argdo: вы контролируете набор.
:windo и :tabdo
:windo удобен для синхронной настройки видимых окон: например, включить относительную нумерацию во всех окнах текущей вкладки.
Код: Выделить всё
:windo set relativenumber " во всех окнах текущей вкладки
:windo diffthis " включить diff-режим во всех видимых окнах
Лучшие практики 2026Тонкость: :bufdo/:argdo оставляют курсор в последнем обработанном буфере. Если хотите вернуться, поставьте метку или используйте Ctrl-^ после.
- Живите в буферах, а не во вкладках. Список открытых файлов - это :ls/:b, а не цепочка вкладок. Буферов могут быть десятки без визуального шума. Вкладки держите для раскладок окон под задачу. Это снимает 90% путаницы новичков.
- Ctrl-^ - самая рентабельная клавиша для парной работы. Реализация <-> тест, исходник <-> заголовок: одно нажатие вместо листания :bn.
- Включите set hidden (в Neovim это дефолт), чтобы свободно переключаться между несохранёнными буферами. Без неё пакетные команды и :bn будут спотыкаться о модифицированные буферы.
- Для пакетных правок предпочитайте :argdo ... | update поверх :bufdo - arglist даёт явный, контролируемый набор файлов и не цепляет случайно открытые буферы.
- Не дублируйте то, что лучше делает quickfix. В 2026 для замены "по всему проекту" чаще берут связку :grep/ripgrep -> quickfix -> :cfdo, а не :bufdo/:argdo по всем буферам - это точнее и не требует заранее открывать файлы. Подробно - в главе 14.
- Настройте 'switchbuf' под себя. Эта опция управляет тем, как :sb, переходы из quickfix (:cc, прыжок по <CR> в списке) и подобные команды выбирают окно для буфера: useopen - переиспользовать окно, где буфер уже открыт; usetab - искать и в других вкладках; split/newtab - открывать в новом сплите/вкладке. Например, set switchbuf=useopen,usetab избавляет от лишних дублей окон при работе с quickfix-связкой.
- В Neovim замену часто удобнее делать интерактивно через современные плагины глобального find-and-replace (например, grug-far.nvim), где результаты редактируемы и синкаются обратно в файлы; но базовый :argdo/:cfdo остаётся фундаментом, который работает всегда и везде. Плагины и fuzzy-навигация по файлам - глава 15 и 28.
- Используйте gt/gT и {N}gt осознанно: давайте вкладкам смысл ("тесты", "git", "доки"), а не превращайте их в свалку файлов.
- Заглавные Ctrl-W H/J/K/L - быстрый способ переверстать сплиты, когда горизонтальный лэйаут вдруг оказался неудобным; не пересоздавайте окна вручную.
- "Я закрыл окно и потерял файл". Нет: :close/Ctrl-W c/:q закрывают окно, буфер остаётся в :ls. Вернитесь к нему через :b.
- :bd != "закрыть вкладку/окно". :bd выгружает буфер из списка. Если он был виден, Vim подставит другой буфер в это окно.
- Использование вкладок как списка файлов. Признак неверной модели. Файлы - это буферы; вкладки - лэйауты.
- :argdo/:bufdo без флага e в замене. На первом же файле без совпадения команда оборвётся с E486: Pattern not found. Добавляйте s/.../.../ge.
- Забыли | update после пакетной правки. Изменения повисают в несохранённых буферах; при выходе Vim ругается. Включайте update прямо в команду.
- set nohidden и пакетные команды. При выключенной hidden переход на несохранённый буфер запрещён, и :bufdo/:bn падают. Включите hidden (или сохраняйтесь по ходу).
- Путаница :bn (буфер) и :tabn/gt (вкладка) и Ctrl-W w (окно). Это три разные навигации по трём разным сущностям. Держите модель в голове.
- :bufdo затрагивает лишнее. Справка, netrw, временные буферы тоже попадают под раздачу. Для безопасности - :argdo по явному arglist.
- Карусель буферов. Откройте vim file1 file2 file3 (любые три файла). Выполните :ls, разберите флаги у каждого. Попрыгайте :bn/:bp, затем переключитесь по имени :b file2<Tab>. Наконец, отработайте Ctrl-^: прыгните в file1, потом в file3, и убедитесь, что Ctrl-^ качается между двумя последними.
- Сплит-навигация вслепую. В одном файле сделайте :vs, затем :sp в правом окне - получится три окна. Не глядя на мышь, обойдите все три через Ctrl-W h/j/k/l. Уравняйте их Ctrl-W =, разверните одно Ctrl-W _, верните Ctrl-W =, схлопните до одного Ctrl-W o.
- Переверстка. Сделайте горизонтальный сплит :sp, затем превратите его в вертикальный, переместив одно из окон заглавной командой Ctrl-W L (или Ctrl-W H). Поменяйте окна местами через Ctrl-W x.
- Вкладка как контекст. Создайте :tabnew, откройте в ней файл и его "тест" рядом через :vs. Переключайтесь между основной вкладкой и новой через gt/gT. Выполните :tabs и прочитайте раскладку. Закройте лишнее через :tabonly.
- Пакетная замена через arglist. Скопируйте папку с несколькими текстовыми файлами. Выполните :args путь/**/*.txt, проверьте :args, затем :argdo %s/foo/bar/ge | update. Убедитесь :ls, что не осталось несохранённых +-буферов.
- Синхронная настройка окон. Откройте 2-3 сплита и выполните :windo set relativenumber. Убедитесь, что нумерация включилась во всех видимых окнах. Затем :windo set norelativenumber.
- В Vim buffer != window != tab. Буфер - файл в памяти; окно - вид на буфер; вкладка - раскладка окон. Это самая важная мысль главы.
- Буферы - это и есть ваш список открытых файлов: :e открывает, :ls показывает, :b/:bn/:bp переключают, Ctrl-^ качается с предыдущим, :bd выгружает. Список открытых файлов живёт в буферах, а не во вкладках.
- Окна дают видеть несколько мест сразу: :sp/:vs создают сплиты, Ctrl-W h/j/k/l навигируют, Ctrl-W +/-/= меняют размер, Ctrl-W r/x/o/c переставляют и закрывают. Закрытие окна не трогает буфер.
- Вкладки - это лэйауты под подзадачу, а не список файлов: :tabnew, gt/gT, {N}gt. Использовать их как "ещё один открытый файл" - антипаттерн.
- :args задаёт явный, упорядоченный набор файлов для пакетной работы.
- Пакетные :bufdo/:argdo/:windo/:tabdo прогоняют одну команду по множеству; канонический рецепт замены - :argdo %s/old/new/ge | update. Для замены по всему проекту в 2026 чаще удобнее quickfix + :cfdo (глава 14).