Поиск в Vim: от /слова до замены по всему проекту
Разбираем, как в Vim устроен поиск: от простого / в открытом файле до замены тысяч вхождений в проекте с ripgrep и quickfix. Без магии, только привычки, которые экономят часы работы.

Поиск в редакторе кажется мелочью, пока не приходится искать одно и то же слово в полутысяче файлов проекта на PHP или JS. В Vim и Neovim поиск устроен слоями: от простого поиска по текущему файлу до запросов по всему рабочему каталогу через внешние утилиты. Разберём все уровни по порядку, без лишних плагинов, которые не нужны большинству разработчиков.
Поиск в открытом файле: /, ?, * и навигация n/N
В нормальном режиме клавиша / переводит Vim в режим поиска вперёд по файлу, ? ищет назад. После ввода шаблона и нажатия Enter курсор перескакивает на первое совпадение, а клавиши n и N двигают его к следующему и предыдущему результату. Если курсор стоит на нужном слове, звёздочка * ищет его вперёд по всему файлу целиком, а g* делает то же самое, но ищет слово как часть более длинных слов.
:set hlsearchвключает подсветку всех совпадений (в Neovim включено по умолчанию):nohснимает подсветку без сброса самого поиска/слово\cищет без учёта регистра,\Cпринудительно с учётом
:noh на Esc в конфиге (nnoremap <esc> :noh<cr>): подсветка после десятого поиска в файле начинает мешать больше, чем помогает.Регулярные выражения: чем синтаксис Vim отличается от PCRE
Поиск в Vim по умолчанию работает в собственном диалекте регулярных выражений, который местами не совпадает с привычным PCRE из PHP, JS или ripgrep. Главное отличие: группы и квантификаторы нужно экранировать обратным слэшем, если не включён режим \v (very magic), который делает синтаксис почти обычным.
| Задача | Vim (magic) | Vim (\v) / PCRE |
|---|---|---|
| Группа | \(php\|js\) | (php|js) |
| Один или больше | a\+ | a+ |
| Любая цифра | \d | \d |
| Начало слова | \<term | \<term |
Если регулярка не ищется так, как ожидалось, первое, что стоит проверить: не забыт ли режим \v в начале шаблона. С ним большинство привычек из PHP и JS переносятся почти без изменений.
Отдельно стоит запомнить пару меток \zs и \ze, у них нет прямого аналога в PCRE, и именно их отсутствие чаще всего ломает перенесённые из других языков регулярки. \zs отмечает начало фактического совпадения внутри более длинного шаблона, а \ze отмечает конец, то есть всё, что окружает эти метки, участвует в поиске контекста, но не входит в подсвеченное и заменяемое совпадение. Например, шаблон function\zs.*\ze( найдёт только имя функции между словом function и открывающей скобкой, не трогая саму скобку и ключевое слово, и это ровно то, что в PCRE обычно делают через lookahead и lookbehind.
vimgrep и quickfix: поиск по проекту без единого плагина
Команда :vimgrep ищет шаблон во всех переданных ей файлах и складывает результаты в quickfix-список, встроенный механизм Vim для любых списков вида «файл плюс строка».
:vimgrep /TODO/ **/*.php
:copenКоманда выше ищет TODO во всех PHP-файлах рабочего каталога и подкаталогов, а :copen открывает окно со списком найденных мест. Внутри quickfix-окна :cnext и :cprev двигают курсор по результатам, Enter на строке открывает файл в нужном месте. У :vimgrep есть честный минус: он построчно читает каждый файл сам, на репозитории в несколько тысяч файлов это заметно медленнее внешнего поисковика.
:grep с ripgrep вместо встроенного движка
Для больших проектов Vim умеет делегировать поиск внешней программе через :grep, которая по умолчанию зовёт системный grep, но её легко переключить на ripgrep: быстрее, с поддержкой .gitignore из коробки и без лишних системных файлов в результатах.
set grepprg=rg\ --vimgrep\ --smart-case
set grepformat=%f:%l:%c:%mПосле такой настройки :grep TODO отработает за доли секунды даже на монорепозитории, а результат попадёт в тот же quickfix и откроется через :copen. Если ripgrep не установлен, в Debian и Ubuntu он ставится одной командой apt install ripgrep, в Arch пакет называется так же.
:grep каждый раз перезаписывает список, а не дополняет его, так что старые результаты не потеряются случайно, зато легко забыть, что искали последним.Замена по всему проекту: argdo, bufdo и cgn
Классический способ заменить в одном файле: :%s/старое/новое/g. Буква g в конце означает замену всех вхождений в строке, без неё заменится только первое. Для проекта из нескольких файлов Vim использует arglist, список файлов, с которым можно работать как с одним буфером.
:args **/*.php
:argdo %s/old_name/new_name/ge | updateФлаг e в конце команды подстановки подавляет ошибку на файлах, где совпадений не нашлось, без него :argdo остановится на первом же таком файле. :bufdo работает так же, но по уже открытым буферам, а не по arglist.
- Нужна замена с подтверждением на каждое вхождение: добавьте флаг
cк:s - Нужно заменить только выделенные вручную вхождения в файле: встаньте на первое совпадение и используйте
cgn, затемnи точку для повтора на следующих - Собрали список мест через
:vimgrepи хотите заменить только в них::cfdo %s/.../.../ge | updateпройдёт по файлам из quickfix, а не из arglist
Что изменилось с приходом LSP и Telescope в 2026 году
Встроенный LSP-клиент в Neovim и плагины вроде Telescope или fzf-lua не заменяют описанные выше команды, они решают другую задачу: поиск по смыслу (где объявлена функция, кто её вызывает), а не по тексту. Поиск по строке остаётся самым быстрым способом найти текстовое совпадение, когда вы точно знаете, что искать, и не хотите ждать индексации проекта. На практике разработчики комбинируют оба подхода: LSP для навигации по коду, :grep с ripgrep для текстового поиска и массовой замены.
Telescope добавляет к этому удобный интерактивный слой: его модуль live_grep запускает тот же ripgrep, но результаты обновляются на каждое нажатие клавиши и показываются в отдельном окне с предпросмотром файла. Для разового поиска это удобнее голого :grep, который требует сначала набрать весь запрос и только потом увидеть результат. Для автоматизированной замены по десяткам файлов quickfix и :cfdo остаются быстрее, потому что не требуют ручного подтверждения на каждый файл.
Полезные настройки в vimrc, которые экономят время каждый день
Несколько строк в конфиге убирают большую часть ручной рутины поиска. Опция incsearch показывает совпадение прямо во время набора шаблона, не дожидаясь Enter, это особенно удобно при составлении регулярки, когда проще увидеть промежуточный результат, чем держать синтаксис в голове. Опция wrapscan, включённая по умолчанию, заставляет поиск переходить в начало файла после последнего совпадения, вместо того чтобы останавливаться на границе.
set incsearchпоказывает совпадения во время набора шаблона, до нажатия Enterset ignorecaseиset smartcaseвместе дают разумное поведение по умолчанию для большинства запросовnnoremap <leader>r :%s/<C-r><C-w>/подставляет слово под курсором прямо в команду замены, не набирая его вручную
Эти три настройки не требуют никаких внешних зависимостей и работают одинаково в Vim и Neovim без установки единого плагина, что делает их первым кандидатом в минимальный конфиг на новом сервере.
Частые вопросы
Почему :vimgrep работает медленнее, чем :grep с ripgrep?
Vimgrep читает и разбирает каждый файл средствами самого Vim, это универсально, но медленно на больших деревьях файлов. Ripgrep написан на компилируемом языке, параллелит обход директорий и сразу уважает .gitignore, поэтому на том же проекте он отрабатывает в разы быстрее.
Как искать с учётом регистра по умолчанию, но игнорировать его для запросов с маленькими буквами?
Добавьте в конфиг set ignorecase и set smartcase. Это включит поиск без учёта регистра, но если в шаблоне встретится хотя бы одна заглавная буква, поиск автоматически станет точным по регистру.
Чем argdo отличается от cfdo при замене по нескольким файлам?
Argdo проходит по arglist, списку файлов, который вы явно задали командой args. Cfdo проходит по файлам из текущего quickfix-списка, то есть по результатам последнего vimgrep или grep. Если список файлов для замены совпадает со списком результатов поиска, cfdo удобнее, не нужно собирать arglist вручную.



