Neovim глубоко: модальность, LSP, treesitter и LazyVim
Если tmux и Omarchy — это среда, то Neovim — место, где происходит сама работа. И это единственный инструмент в стеке, к которому относятся как к религии: кто-то бросает после первого часа («как из него выйти?!»), кто-то через месяц уже не представляет, как редактировал код иначе. Разница между этими людьми не в таланте, а в том, поняли ли они одну вещь: Vim — это не редактор с хоткеями. Это язык.
В краткой версии учебника мы советовали «начни с VS Code, Neovim добавь позже». Позже наступило. В этой главе разбираем модальность как грамматику — оператор плюс движение, текст-объекты, повтор через точку, макросы, регистры — а затем современный слой, который превращает vim в полноценную IDE: LSP, treesitter, fuzzy-поиск. Конфигурацию покажем на примере LazyVim — дистрибутива, который даёт всё это из коробки, не заставляя собирать редактор из двухсот плагинов вручную.
Честное предупреждение из практики: первые три-четыре дня ты будешь медленнее, чем в своём старом редакторе. Это нормально, это цена переучивания мышечной памяти. На второй неделе скорость сравняется, через месяц — уйдёт вперёд и уже никогда не вернётся обратно. В конце главы есть план тренировки на две недели, который проведёт через этот провал.
Модальность как язык: оператор + движение
Заголовок раздела «Модальность как язык: оператор + движение»В обычном редакторе у тебя один режим: печатаешь — текст появляется, хочешь удалить — жмёшь Delete по одному символу или выделяешь мышью. В Vim режимов несколько: normal (навигация и команды), insert (ввод текста), visual (выделение), command (команды через :). Культура Vim: insert-режим — это краткие вылазки. Большую часть времени ты в normal-режиме, где клавиши — не буквы, а глаголы и существительные.
Грамматика такова: оператор + движение (или текст-объект). Операторы: d — удалить (delete), c — изменить (change: удалить и перейти в insert), y — скопировать (yank), > — сдвинуть вправо, v — выделить. Движения: w — слово вперёд, b — слово назад, $ — конец строки, 0 — начало, % — парная скобка, } — следующий абзац. Складываешь — получаются предложения:
dw — удалить слово (delete word)d$ — удалить до конца строкиciw — изменить слово целиком (change inner word), курсор где угодно внутри словаdi" — удалить содержимое внутри кавычек, сами кавычки оставитьdt, — удалить до запятой (delete till comma), не включая еёya( — скопировать всё внутри круглых скобок вместе со скобкамиОбрати внимание на ciw против dw: dw удаляет от курсора до конца слова, а ciw — слово целиком, где бы курсор внутри него ни стоял. Это и есть текст-объекты: iw — inner word, i" — внутри кавычек, a( — вокруг скобок включая их, ip — абзац, it — внутри XML/HTML-тега. Текст-объекты оперируют смысловыми единицами, а не позициями курсора. Ты больше не думаешь «поставить курсор сюда, выделить досюда» — ты думаешь «поменять строку в кавычках» и нажимаешь ci".
Повтор, макросы, регистры: автоматизация без скриптов
Заголовок раздела «Повтор, макросы, регистры: автоматизация без скриптов»Точка (.) — самая недооценённая клавиша Vim. Она повторяет последнее изменение целиком. Связка ciw + новое слово + Esc, потом n (следующее совпадение поиска) и . — это переименование по файлу без всякого «заменить все»: ты видишь каждую замену и решаешь, применять или пропустить (n — пропустить, . — применить). Получается интерактивный search-and-replace, который контролируешь ты, а не диалоговое окно.
Макросы — запись последовательности нажатий в регистр и её воспроизведение. qa — начать запись в регистр a, дальше делаешь правку один раз, q — стоп. Теперь @a повторяет всю последовательность, а 10@a — десять раз. Классический сценарий: пришёл CSV из пятидесяти строк, из каждой надо сделать строку конфига. В обычном редакторе — двадцать минут механической работы или написание скрипта. В Vim: один макрос, 49@a, готово за десять секунд.
Регистры — это не один буфер обмена, а десятки именованных ячеек. "ay — скопировать в регистр a, "ap — вставить из него. Безымянный регистр " хранит последнее удаление (удалённое через d — тоже копируется!). Регистр "+ — системный буфер обмена: "*y или "+y отправляет текст наружу, в Slack или браузер. Есть и специальные: регистр 0 всегда хранит последний yank (спасает, когда после копирования что-то удалил и безымянный регистр перезаписался).
Jumplist и marks — навигация по собственной истории. Каждый прыжок (поиск, gd, %) запоминается: Ctrl+o — назад по прыжкам, Ctrl+i — вперёд. Прыгнул по gd в определение, посмотрел, Ctrl+o — вернулся, где был. Метки: ma ставит локальную метку a, `a — прыжок к ней; заглавные (mA) — глобальные, между файлами. В связке jumplist + marks навигация по незнакомой кодовой базе превращается в «прыгай и возвращайся», без мыши и без поиска вкладки глазами.
LSP под капотом: как работает «перейти к определению»
Заголовок раздела «LSP под капотом: как работает «перейти к определению»»Когда ты жмёшь gd (goto definition) в Neovim с LazyVim, происходит не магия, а RPC-запрос. LSP — Language Server Protocol — это стандарт, придуманный Microsoft для VS Code и ставший универсальным: редактор (клиент) запускает отдельный процесс — языковой сервер — и общается с ним по JSON-RPC поверх stdio.
┌──────────────┐ JSON-RPC ┌────────────────────────┐│ Neovim │ ◄──────────► │ typescript-language- ││ (LSP-клиент) │ поверх stdio│ server (отдельный ││ │ │ процесс, свой парсер, ││ gd ──────────┼──────────────►│ свой индекс проекта) ││ textDocument│ definition │ ││ /definition │ │ │└──────────────┘ └────────────────────────┘Клиент шлёт textDocument/definition с координатами курсора, сервер — который действительно распарсил твой проект и построил семантическую модель — отвечает «определение вот в этом файле, вот на этой строке». Те же запросы стоят за Space ca (code actions: «импортируй недостающее», «переименуй безопасно»), за диагностикой (красные подчёркивания — это textDocument/publishDiagnostics, который сервер пушит сам) и за автодополнением. Важный вывод: умность живёт не в редакторе, а в сервере. Поэтому один и тот же typescript-language-server одинаково хорошо работает и в Neovim, и в VS Code — разница только в клиенте.
В LazyVim установкой серверов управляет mason — пакетный менеджер LSP-серверов, форматтеров и линтеров. :Mason открывает TUI, где ставится всё: от typescript-language-server до lua-language-server и prettierd. Клиентская часть в Neovim встроенная (vim.lsp), а конфигурация привязки серверов к файлам — через nvim-lspconfig, но в LazyVim она уже сделана за тебя.
Treesitter: редактор, который понимает синтаксис
Заголовок раздела «Treesitter: редактор, который понимает синтаксис»Классическая подсветка синтаксиса в редакторах — это регулярные выражения: «слово function покрасить в фиолетовый». Регулярки не понимают структуру, поэтому вложенные шаблонные строки или хитрый синтаксис регулярно ломают подсветку. Treesitter подходит иначе: он парсит файл в настоящее AST — абстрактное синтаксическое дерево — той же природы, что строит компилятор, и делает это инкрементально: после правки перепарсивается только изменённая ветка, поэтому работает мгновенно даже на больших файлах.
Раз есть дерево — подсветка становится точной: редактор знает, что этот name — параметр функции, а тот — поле объекта, хотя регулярно они неотличимы. Но подсветка — только начало. На AST работают текст-объекты нового поколения (af — функция целиком, ac — класс), умный инкрементальный выбор (нажатие расширяет выделение по узлам дерева: выражение → оператор → блок → функция) и навигация «следующая функция». Это принципиальный сдвиг: редактор перестаёт видеть текст как мешок символов и начинает видеть код.
Fuzzy-поиск и LazyVim: современный слой
Заголовок раздела «Fuzzy-поиск и LazyVim: современный слой»Fuzzy-поиск — третий столп современного Neovim после LSP и treesitter. Идея: в проекте на тысячи файлов ты не помнишь путей, ты помнишь куски имён. Печатаешь usrctrl — находится src/components/UserController.tsx. В LazyVim за это отвечает snacks picker: Space Space — файлы проекта, Space / — поиск по содержимому (grep через ripgrep), Space fr — недавние файлы. Это три самые нажимаемые комбинации в LazyVim; навигация «по проекту» после них почти перестаёт существовать как задача.
Структура конфигурации LazyVim — это хороший пример того, как должен выглядеть модульный конфиг:
~/.config/nvim/├── init.lua # точка входа: подключает lazy.nvim и LazyVim└── lua/ ├── config/ │ ├── options.lua # опции редактора (vim.opt) │ ├── keymaps.lua # твои кеймапы │ └── autocmds.lua # автокоманды └── plugins/ └── *.lua # один файл = один плагин или группаКаждый файл в lua/plugins/ возвращает таблицу-спецификацию плагина, и lazy.nvim сам собирает их в общий граф загрузки. Добавить плагин — создать файл, отключить — вернуть спеку с enabled = false. Тебе не нужно читать весь дистрибутив: свои правки кладёшь рядом, и они накладываются поверх дефолтов — та же идея слоёв, что и в Omarchy.
-- lua/plugins/example.lua — переопределяем поведение snacks pickerreturn { { "folke/snacks.nvim", opts = { picker = { -- показывать скрытые файлы в поиске файлов hidden = true, }, }, },}Ключевые плагины, которые стоит знать по имени:
- flash — мгновенные прыжки: жмёшь
s, печатаешь два символа цели, получаешь подсвеченные метки, жмёшь метку — курсор там. Заменяет десятки нажатийw/bодним прыжком. - mini.surround — операции с «обрамлением»:
gsaiw"— обернуть слово в кавычки (surround add inner word),gsd"— удалить кавычки,gsr"'— заменить кавычки на одинарные. Это оператор surround, встроенный в ту же грамматику. - blink.cmp — автодополнение:
Enterпринимает вариант,Tab/Shift+Tab— навигация по сниппетам и плейсхолдерам. Источники — LSP, пути, буфер, сниппеты. - neo-tree — файловое дерево (
Space e). Используй его для обзора структуры и операций над файлами, а не для навигации — для навигации есть picker. - gitsigns + lazygit — gitsigns рисует изменения в gutter (плюсики и тильды у строк, хунки можно стейджить и откатывать прямо из редактора), а
Space ggоткрывает lazygit — полноценный TUI для git: staging по хункам, rebase, ветки.
Шпаргалки
Заголовок раздела «Шпаргалки»Движение (normal-режим):
| Клавиши | Действие |
|---|---|
h j k l |
влево / вниз / вверх / вправо |
w / b / e |
слово вперёд / назад / конец слова |
0 / $ / % |
начало строки / конец / парная скобка |
gg / G / {line}G |
начало файла / конец / строка N |
Ctrl+d / Ctrl+u |
полстраницы вниз / вверх |
Ctrl+o / Ctrl+i |
назад / вперёд по jumplist |
Редактирование:
| Клавиши | Действие |
|---|---|
ciw / di" / dt, |
изменить слово / удалить внутри кавычек / удалить до запятой |
dd / yy / cc |
строку: удалить / скопировать / изменить |
. |
повторить последнее изменение |
u / Ctrl+r |
undo / redo |
qa … q, @a |
записать макрос в a, воспроизвести |
"+y / "+p |
в системный буфер / из него |
gsa/gsd/gsr (mini.surround) |
добавить / удалить / заменить обрамление |
Файлы и поиск (LazyVim):
| Клавиши | Действие |
|---|---|
Space Space |
fuzzy-поиск файлов |
Space / |
grep по проекту |
Space fr |
недавние файлы |
Space e |
файловое дерево (neo-tree) |
s{2 символа}{метка} (flash) |
прыжок к видимой цели |
Space gg |
lazygit |
LSP:
| Клавиши | Действие |
|---|---|
gd / gr |
к определению / ссылки на символ |
K |
документация под курсором |
Space ca |
code actions |
Space cr |
переименование символа |
[d / ]d |
предыдущая / следующая диагностика |
Буферы и окна:
| Клавиши | Действие |
|---|---|
Space bd |
закрыть буфер |
Shift+h / Shift+l |
предыдущий / следующий буфер |
Space - / Space | |
сплит горизонтально / вертикально |
Ctrl+h/j/k/l |
фокус между окнами |
План тренировки на две недели
Заголовок раздела «План тренировки на две недели»Дни 1–3: выживание. Только hjkl, i, Esc, dd, u, :w, :q. Запрети себе стрелки и мышь. Будет больно и медленно — так и задумано. Вечером третьего дня добавь w/b.
Дни 4–7: язык. Весь день работаешь через ciw, di", dt. и точку-повтор. Каждый раз, когда хочется выделить мышью, — остановись и подумай, какой текст-объект описывает цель. В конце недели — первый макрос на реальной рутинной задаче.
Дни 8–10: навигация. Space Space и Space / вместо дерева файлов, gd/Ctrl+o для чтения чужого кода, flash-прыжки вместо wwwww. Цель — ни разу не открыть neo-tree ради перехода в файл.
Дни 11–14: интеграция. Работаешь над pet-проектом целиком в Neovim: код, тесты в соседнем окне, gitsigns для стейджинга, lazygit для коммитов. К концу второй недели скорость должна сравняться со старым редактором; дальше она только растёт.
Типичные ошибки и грабли
Заголовок раздела «Типичные ошибки и грабли»- Жить в insert-режиме. Новичок заходит в insert и стрелками двигается внутри него. Это VS Code с лишними шагами. Правило: закончил печатать —
Esc. Insert — это глагол короткого действия, не состояние по умолчанию. - Учить хоткеи списком. Двадцать комбинаций в день не выучиваются. Формула дня: одна новая «фраза» (
ciw) — и используешь её до автоматизма. - Собирать конфиг с нуля в первый месяц. Самописный конфиг без понимания, что в нём происходит, — главная причина «Neovim сломался, вернулся в VS Code». LazyVim как база + точечные overrides — правильный путь.
- Ставить LSP-серверы руками в обход mason. Потом версии разъезжаются, сервер не стартует, и непонятно, чей это сбой.
:Mason— единая точка установки и диагностики (:LspInfo,:checkhealth). - Игнорировать регистры и терять скопированное. Удалил строку после копирования — безымянный регистр перезаписался. Хорошо: копируй важное в именованный регистр (
"ay) или помни, что последний yank всегда в регистре0. - «Заменить все» там, где нужна точка. Слепой
:%s/foo/bar/gв коде с контекстами — источник тихих багов.n+.даёт тебе контроль над каждой заменой — и это быстрее, чем откатывать плохую замену.
Вопросы на собеседовании
Заголовок раздела «Вопросы на собеседовании»- Что такое LSP и почему он изменил экосистему редакторов? Протокол, разделяющий редактор и «умный» языковой движок: клиент шлёт JSON-RPC запросы (definition, completion, diagnostics), сервер с распарсенной моделью проекта отвечает. До LSP каждый редактор писал свою интеграцию с каждым языком (N×M), после — сервер пишется один раз и работает везде (N+M).
- Чем treesitter отличается от подсветки на регулярках? Регулярки видят текст, treesitter строит AST — настоящее синтаксическое дерево, инкрементально перепарсиваемое при правках. Точная подсветка, структурные текст-объекты и навигация по узлам дерева.
- Объясни
ciwколлеге, который не знает Vim. «Change inner word»: операторc(изменить) применён к текст-объектуiw(слово целиком). Редактор воспринимает команды как грамматику «глагол + существительное», поэтому зная пять операторов и пять объектов, ты знаешь двадцать пять команд. - Что делает
.(точка) и почему её называют самой мощной клавишей? Повторяет последнее изменение как единое действие. В связке с поиском (n/.) даёт интерактивную замену с контролем каждого случая; с макросами и операторами превращает редактирование в композицию повторяемых действий. - Как устроен автокомплит в Neovim? Движок (например, blink.cmp) собирает кандидатов из источников — LSP-сервер, сниппеты, пути, слова буфера — ранжирует и показывает попап;
Enterпринимает,Tabдвигается по плейсхолдерам сниппета. Семантическая часть кандидатов приходит от LSP-сервера. - Чем макрос в Vim отличается от скрипта? Макрос — записанная последовательность нажатий в регистре, воспроизводимая
@a(иN@aдля повторов). Не требует языка программирования, пишется за секунды, идеален для одноразовой механической трансформации данных прямо в буфере. - Что такое jumplist и чем он отличается от undo? Jumplist — история прыжков курсора (поиск,
gd,%), по ней ходятCtrl+o/Ctrl+i. Это навигация в пространстве, а undo — навигация во времени изменений; они ортогональны.
Практика
Заголовок раздела «Практика»- Пройди
vimtutor(команда в терминале) целиком, не пропуская упражнений. Критерий: все шесть уроков закрыты,hjklиспользуется без взгляда на клавиатуру. - Напиши первый override для LazyVim: файл в
lua/plugins/, меняющий одну опцию любого плагина (например,hidden = trueв picker). Критерий::Lazyпоказывает плагин, поведение изменилось, конфиг LazyVim не отредактирован напрямую. - Макрос-баттл. Возьми список из 30 строк (например, пары «имя — email») и преврати его в YAML-список одним макросом. Критерий: меньше минуты, ни одной ручной правки после
29@a. - День без мыши в коде. Целый рабочий день над pet-проектом: навигация только
Space Space,gd,Ctrl+o, flash. Запиши три момента, где пришлось открыть старый редактор, — это твой план доучивания. - Настрой LSP для стека pet-проекта через
:Mason(typescript, tailwind, prisma). Критерий:gdработает по проекту, диагностика прилетает,:LspInfoпоказывает прикреплённые серверы.
Что почитать
Заголовок раздела «Что почитать»- LazyVim — документация — структура дистрибутива, кеймапы по умолчанию, список включённых плагинов.
- Neovim: built-in LSP — официальная документация по клиентской части LSP.
- Спецификация Language Server Protocol — как устроены запросы
textDocument/definitionи компания. - tree-sitter — проект парсеров — как строится инкрементальное AST и почему это быстро.
- mason.nvim — пакетный менеджер LSP-серверов, форматтеров и линтеров.
- Practical Vim, Drew Neil — лучшая книга про философию редактирования: операторы, точка, макросы.