Введение

Когда я начинал свой путь с Neovim как Python-разработчик, я столкнулся с проблемой: в русскоязычном сегменте практически нет материалов о том, как собрать свой конфиг с нуля.
Есть готовые дистрибутивы (NvChad, LazyNvim, AstroNvim), есть переводы англоязычных статей, но нет пошагового руководства на русском, которое объясняет почему и как устроен современный конфиг Neovim.

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

Для кого этот цикл статей?

Эта серия статей для вас, если вы:

  • Уже знакомы с базовыми движениями Vim/Neovim (hjkl, моды, базовые команды)

  • Хотите перейти с готовых дистрибутивов на свой конфиг

  • Устали копировать чужие настройки, не понимая, как они работают

  • Нуждаетесь в базовом, но расширяемом конфиге под свои задачи

Важно: Это не учебник по Vim - я предполагаю, что вы уже умеете работать в этом редакторе и он у вас уже установлен. Мы сосредоточимся именно на создании современного конфига на Lua.

Что мы построим?

К концу этого цикла у вас будет:

  • Чистый, понятный конфиг на Lua, который вы понимаете

  • Менеджер плагинов lazy.nvim

  • Современное автодополнение через blink.cmp

  • Подсветка синтаксиса через Treesitter

  • Нативный LSP

  • Форматтеры и линтеры

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

Технические детали

Все примеры будут показаны на Arch Linux (btw) и Neovim v0.11.5, но концепции одинаковы для всех платформ.

Требования:

  • Neovim 0.10 или выше

  • Git

  • Базовое понимание Lua

Начинаем с чистого листа

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

mv ~/.config/nvim ~/.config/nvim.backup
mv ~/.local/share/nvim ~/.local/share/nvim.backup
mv ~/.local/state/nvim ~/.local/state/nvim.backup
mv ~/.cache/nvim ~/.cache/nvim.backup

Это сбросит все настройки Neovim к базовому состоянию.

Структура конфига: философия модульности

Существует 2 подхода к созданию конфига Neovim:

  • Хранить все настройки в одном большом init.lua

  • Разбить конфиг на маленькие модули, каждый из которых будет содержать свою логику

Современные конфиги Neovim строятся модульно. Вместо одного огромного файла на 1000+ строк, мы разобьём настройки на логические блоки.

Создадим базовую структуру:

mkdir -p ~/.config/nvim/lua/core
touch ~/.config/nvim/init.lua

Итоговая структура будет выглядеть так:

~/.config/nvim/
├── init.lua
└── lua
    └── core

Почему именно так?

  • init.lua - Neovim читает этот файл автоматически при запуске и он является точкой входа

  • lua/ - Директория, из которой Neovim может импортировать модули

  • core/ - Директория с основными настройками редактора (опции, горячие клавиши, цветовая схема и т.п.)

Пока что оставим init.lua пустым и создадим наш первый модуль, который будет содержать базовые настройки:

touch ~/.config/nvim/lua/core/options.lua

И подключим его в ~/.config/nvim/init.lua:

require("core.options")

require() в Lua работает как import в Python - он ищет файлы в директории lua/ и выполняет их код.

Обратите внимание:

  • Путь lua/core/options.lua превращается в require("core.options")

  • Расширение .lua не указывается

  • Слэши заменяются точками

Теперь давайте наполним эти модули содержимым.

options.lua: настраиваем редактор

Откроем ~/.config/nvim/lua/core/options.lua и начнём его наполнять.

Базовая структура

local opt = vim.opt

vim.opt - это современный API для установки опций в Neovim (аналог set из Vimscript). Сохраняем его в переменную opt для краткости.

Отступы и табуляция

opt.expandtab = true -- Преобразовать табы в пробелы
opt.smarttab = true -- Умная вставка табов
opt.smartindent = true -- Автоматические отступы
opt.tabstop = 2 -- Какой ширины выглядит символ таба в файле
opt.softtabstop = 2 -- На сколько колонок двигает/удаляет Tab и Backspace
opt.shiftwidth = 2 -- Размер отступа для >> и <<

Эти настройки определяют, как Neovim обрабатывает отступы. Здесь я использую 2 пробела (для Lua чаще всего используют 2 или 4). Позже Treesitter сможет автоматически перезаписывать эти значения в зависимости от типа файла.

Отображение

opt.number = true -- Показывать номера строк
opt.numberwidth = 2 -- Ширина колонки с номерами
opt.fillchars = { eob = " " } -- Убрать символ ~ в конце буфера (визуальный мусор)

Помимо opt.number существует opt.relativenumber, который показывает номера строк относительно текущей строки. Это полезно для быстрого перемещения по строкам (10j, 5k), чтобы не считать строки в уме.

Причём мы можем комбинировать эти опции:

opt.number = true
opt.relativenumber = true

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

Обычный режим:        С relativenumber:      Комбинация обоих:
1  line one           3  line one            3  line one
2  line two           2  line two            2  line two
3  line three         1  line three          1  line three
4  line four (cur)    0  line four (cur)     4  line four (cur)
5  line five          1  line five           1  line five
6  line six           2  line six            2  line six

Поиск

opt.ignorecase = true -- Игнорировать регистр при поиске
opt.smartcase = true -- Но учитывать, если в запросе есть заглавные буквы

Как это работает: поиск /hello найдёт hello, Hello и HELLO. Но поиск /Hello найдёт только Hello, но не hello и HELLO.

Интерфейс

opt.termguicolors = true -- Поддержка 24-bit цветов
opt.mouse = "a" -- Включить мышь во всех режимах
opt.wrap = true -- Автоматический перенос длинных строк

Обратите внимание

  • termguicolors = true - обязательно для современных цветовых схем, ваш эмулятор терминала должен это поддерживать

  • mouse = "a" - можно кликать, скроллить, выделять мышью (если вам так привычнее)

Файлы и буферы

opt.undofile = true -- Сохранять историю отмен между сессиями
opt.swapfile = false -- Отключить swap-файлы (они раздражают)
opt.fileencoding = "utf-8" -- Кодировка файлов
opt.clipboard = "unnamedplus" -- Использовать системный буфер обмена

Самое полезное здесь:

  • undofile = true - можно закрыть файл, открыть через неделю и нажать u для отмены изменений

  • clipboard = "unnamedplus" - y и p работают с системным буфером (Ctrl+C/Ctrl+V)

Важно для Linux: Для работы системного буфера нужен xclip (X11) или wl-clipboard (Wayland):

sudo pacman -S xclip
sudo pacman -S wl-clipboard

Дополнительно

opt.timeoutlen = 400 -- Время ожидания комбинаций клавиш (мс)

timeoutlen - как быстро Neovim должен "понять", что вы закончили комбинацию. Для меня удобно значение в 400мс.

Это базовые опции. Обо всех остальных опциях можно почитать в документации

Полный файл options.lua

Соберём всё вместе:

local opt = vim.opt

opt.expandtab = true
opt.smarttab = true
opt.smartindent = true
opt.tabstop = 2
opt.softtabstop = 2
opt.shiftwidth = 2

opt.number = true
opt.numberwidth = 2
opt.fillchars = { eob = " " }

opt.ignorecase = true
opt.smartcase = true

opt.termguicolors = true
opt.mouse = "a"
opt.wrap = true

opt.undofile = true
opt.swapfile = false
opt.fileencoding = "utf-8"
opt.clipboard = "unnamedplus"

opt.timeoutlen = 400

Совет: Не копируйте всё слепо. Найдите комфортные для себя значения.

Сохраните файл и перезапустите Neovim (:x -> nvim). Ваш редактор уже должен выглядеть иначе - с номерами строк, правильными отступами и работающим буфером обмена.

Проблема: как работать с несколькими файлами?

Откройте два файла одновременно:

nvim ~/.config/nvim/init.lua ~/.config/nvim/lua/core/options.lua

Neovim загрузил оба файла в буферы, но видите вы только init.lua. Как переключиться на второй файл?

В Vim/Neovim для этого есть команды:

  • :bnext (или :bn) - следующий буфер

  • :bprevious (или :bp) - предыдущий буфер

Попробуйте набрать :bnext и нажать Enter - вы переключитесь на options.lua. А теперь :bprevious вернёт вас обратно. Введите :xa, чтобы закрыть все буферы.

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

Здесь нам нужны горячие клавиши (keymaps) - короткие комбинации для частых действий.

core/keymaps.lua: ускоряем работу

Создайте файл ~/.config/nvim/lua/core/keymaps.lua. Начнём с создания вспомогательной функции.

Функция для удобной настройки клавиш

local function map(mode, lhs, rhs, desc_or_opts)
  local opts = {}

  if type(desc_or_opts) == "string" then
    opts = { desc = desc_or_opts }
  elseif type(desc_or_opts) == "table" then
    opts = desc_or_opts
  end
  opts.silent = opts.silent ~= false

  vim.keymap.set(mode, lhs, rhs, opts)
end

Что здесь происходит?

Это обёртка над vim.keymap.set(), которая делает код чище. Вместо:

vim.keymap.set("n", "<leader>w", ":w<CR>", { desc = "Save file", silent = true })

Мы пишем:

map("n", "<leader>w", ":w<CR>", "Save file")
  • mode - режим (n = normal, v = visual, i = insert)

  • lhs - комбинация клавиш, которую нажимаем

  • rhs - команда, которая выполняется

  • desc_or_opts - описание или таблица с опциями

  • silent = true - не показывать команду в командной строке при её выполнении

<CR> - Carriage Return (Enter)

Не забываем подключить этот файл в init.lua:

require("core.options")
require("core.keymaps")

Leader key: ваша главная клавиша

vim.g.mapleader = " "
vim.g.maplocalleader = " "

Leader - это специальная клавиша-префикс для ваших кастомных команд. Я использую Space (пробел), потому что:

  • Легко нажать обеими руками

  • Не конфликтует с базовыми командами Vim

  • Стандарт де-факто в современных конфигах

Мы задаём его заранее, так как он потребуется в б��дущем.

Базовые улучшения

map("n", "<Esc>", ":noh<CR>", "Clear Highlight")

После поиска (/something) Neovim подсвечивает все совпадения. Нажатие Esc уберёт эту подсветку.

Навигация по буферам

Помните нашу проблему с переключением между файлами? Решаем:

map("n", "]b", ":bnext<CR>", "Next Buffer")
map("n", "[b", ":bprevious<CR>", "Previous Buffer")

Теперь вместо :bnext просто нажмите ]b, а вместо :bprevious - [b.

Навигация между окнами

Когда вы разделите экран (:split или :vsplit), нужно быстро переключаться между окнами:

map("n", "<C-h>", "<C-w>h", "Move left")
map("n", "<C-j>", "<C-w>j", "Move down")
map("n", "<C-k>", "<C-w>k", "Move up")
map("n", "<C-l>", "<C-w>l", "Move right")

Стандартно в Vim это Ctrl+w, затем направление (h/j/k/l).

Попробуйте:

  1. Откройте файл: nvim ~/.config/nvim/init.lua

  2. Разделите экран вертикально: :vsplit ~/.config/nvim/lua/core/options.lua

  3. Переключайтесь между окнами: Ctrl+h и Ctrl+l

Полный файл keymaps.lua

local function map(mode, lhs, rhs, desc_or_opts)
  local opts = {}
  if type(desc_or_opts) == "string" then
    opts = { desc = desc_or_opts }
  elseif type(desc_or_opts) == "table" then
    opts = desc_or_opts
  end
  opts.silent = opts.silent ~= false
  vim.keymap.set(mode, lhs, rhs, opts)
end

vim.g.mapleader = " "
vim.g.maplocalleader = " "

map("n", "<Esc>", ":noh<CR>", "Clear Highlight")

map("n", "]b", ":bnext<CR>", "Next Buffer")
map("n", "[b", ":bprevious<CR>", "Previous Buffer")

map("n", "<C-h>", "<C-w>h", "Move left")
map("n", "<C-j>", "<C-w>j", "Move down")
map("n", "<C-k>", "<C-w>k", "Move up")
map("n", "<C-l>", "<C-w>l", "Move right")

Сохраните и перезапустите Neovim. Откройте несколько файлов и попрактикуйтесь.

Что мы сделали?

Давайте подведём итог. Теперь у нас есть:

  • Модульная структура конфига - init.lua + директория lua/core/

  • Настроенный редактор - отступы, номера строк, буфер обмена, история отмен

  • Удобная навигация - быстрое переключение между буферами и окнами

  • Понимание основ - вы знаете, как работает require(), vim.opt и vim.keymap.set()

Ваш конфиг на данный момент выглядит так:

~/.config/nvim/
├── init.lua
└── lua
    └── core
        ├── keymaps.lua
        └── options.lua

Это фундамент, на котором мы будем строить дальше. Он уже функционален - можно работать, редактировать файлы, переключаться между буферами и окнами.

Исходный код конфига можно посмотреть на GitHub

Что дальше?

В следующей части мы:

  • Установим менеджер плагинов lazy.nvim и разберёмся, почему мы используем его вместо встроенного vim.pack

  • Разберёмся с концепцией lazy-loading

  • Настроим автодополнение через blink.cmp

  • Подключим подсветку синтаксиса через Treesitter

Домашнее задание:

  1. Поэкспериментируйте с настройками в options.lua

  2. Добавьте собственные горячие клавиши в keymaps.lua (например, <leader>w для быстрого сохранения файла через :w)

  3. Откройте несколько файлов и попрактикуйтесь в навигации с ]b/[b и Ctrl+hjkl

Полезные ресурсы:

  • :help options - Справка по всем опциям Neovim

  • :help vim.keymap.set - Документация по маппингам

  • :help lua - Введение в Lua API для Neovim