Мы никогда не читаем код как книгу — мы выбираем только конкретные интересующие места. Такие места обычно запоминаются ассоциативно, например по имени функции, строковому литералу, импорту библиотеки, комментарию и т. д. Перейти от ассоциации к файлу, а тем более к конкретной строчке кода не всегда легко. Особенно если оперируешь большим количеством проектов с активно меняющейся кодовой базой. В таких случаях выручает удобный инструмент текстового поиска.
Эффективность такого инструмента определяется как скоростью работы, так и удобством использования. В частности, кастомизация под себя позволяет разгрузить мышление и включить «мышечную память» — когда руки сами нажимают кнопки, а все внимание сосредоточено на обработке результатов поиска. Не все инструменты позволяют провести такую тонкую настройку. Меня зовут Роман Щекин, я работаю руководителем команды разработчиков в VK Cloud, и в этой статье мы с вами поищем серебряную пулю, попробуем достичь сочетания скорости и удобства в виде собранного из кросс-платформенного опенсорса поисковика.
Дисклеймер
Решения по типу SourceGraph в статье не рассматриваются намеренно, речь идет именно об удобстве локального поиска.
Полнофункциональные терминальные решения на базе vim/neovim не рассматриваются, так как имеют довольно высокий порог входа.
Я ни в коем случае не эксперт по bash, поэтому очень приветствую замечания и улучшения по скриптам.
Введение об идеальном поисковике
Прежде чем поделиться своим рецептом, я бы хотел кратко пояснить читателям истоки именно такого видения идеального поисковика. То решение, к которому мы придем в конце статьи, во многом навеяно предыдущим опытом и сформировавшимися привычками. Постараюсь упаковать и то, и другое в пару абзацев! Если же хочется опустить лирику и сразу посмотреть, что получилось в итоге, можете перейти в раздел «Собираем все вместе».
Я начинал свою карьеру как разработчик под Windows в поздних нулевых. По моему субъективному мнению, с инструментами разработки тогда все было хорошо хотя бы по причине Visual Studio 2005. Разработку под Windows эта IDE позволяла делать с высоким уровнем комфорта, все возможности были доступны из GUI, и практически никогда не требовалось оперировать терминалом. Еще на Windows был и есть Total Commander. Это буквально первая программа, которая предстала передо мной на первом персональном (в те времена, скорее, «семейном») компьютере. Азы работы с файлами и каталогами учил в нем, с тех пор всегда ставлю TC одним из первых на свежую инсталляцию ОС от Microsoft. Total Commander — действительно удобный, проработанный и расширяемый файловый менеджер, полных аналогов которому на других платформах пока мне найти не удалось.
К чему я? Одна из киллер-фич TC, по моему мнению, — поиск файлов по имени и содержимому. Я как-то уже упоминал о нем в комментарии — обсуждение и сама статья про Far заслуживают внимания. Основной сценарий использования поиска такой:
Открыть папку с большой кодовой базой или сразу со всеми проектами.
Ввести маску для файлов, например `*.cpp;*.h;*.c`.
Ввести искомую подстроку, например
malloc
.Ознакомиться со списком найденных файлов.
Посмотреть результаты точечно с помощью встроенного просмотрщика (Lister).
Ключевое удобство в том, что можно нажать F3 (хоткей Lister) прямо на строчке в диалоге поиска (не выводя результаты в панель!), чтобы просмотреть содержимое найденного файла. При этом Lister понимает, что его вызвали в контексте поиска, и повторное нажатие на F3 в нем скроллит к искомой строке (в примере это malloc
). Последующие нажатия скроллят к следующему вхождению строки в файле, если таковые имеются. Такая получилась нативная интеграция поисковика с просмотрщиком. По нажатию на F4 открывается редактор, который, кстати, можно выбрать на свой вкус.
В итоге выработалась привычка использовать IDE для проекта, над которым в моменте активно работаю, и Total Commander для широкого поиска по остальной кодовой базе и разнообразным конфигам. Приходилось работать с самыми разными кодовыми базами, порой очень дремучими и запутанными — задачи выполнялись, тулинг не подводил. Эта связка отлично себя показывает на практике при работе в Windows и по сей день.
Но где-то же должна быть завязка статьи? :) Она заключается вот в чем: за последние пару лет я окончательно перешел на MacOS в качестве рабочей ОС. Большую часть времени за работой провожу в ней. И поначалу мне очень недоставало хорошего инструмента для поиска — удобство предыдущего подхода просто нечем было восполнить. Поэтому, когда я наконец-то набрел на «конструктор», из которого можно собрать что-то на свой вкус, именно привычный вариант работы, как в TC, я взял за образец.
Поиск лучшего решения
Итак, на старте мы имеем MacOS + желание быстро и удобно искать текст. Давайте прикинем, что с этим можно сделать.
Что не сработало
Идея собрать что-то под себя пришла не сразу, поэтому я начал с перебора готовых коробочных решений. Каждый из инструментов ниже, помимо всего прочего, позволяет искать файлы по названию и по содержимому. Не хочется утверждать, что это совсем нерабочие варианты, но лично у меня «магии на кончиках пальцев» не случилось.
Midnight Commander. Старичок, ветеран индустрии. Думаю, что если бы когда-то привык к нему, а не к Total Commander, то функциональность вполне устроила бы. Внутри есть очень похожий на TC поисковик (а может, это, наоборот, TC похож на MC, who knows) с похожими хоткеями и UX. Однако встроенный просмотрщик и редактор весьма слабые, а настройка под себя показалась замороченной.
FAR / far2l. Добавил по совету @31415. Во многом похоже на MC, но получше в мелочах: редактор поудобнее, подсветка синтаксиса из коробки имеется, просмотрщик умеет в HEX. При этом сам поиск показался довольно медленным (однопоточный?). В общем, крепкий кандидат, но итог поисков мне нравится больше.
Commander One. Один из лучших клонов Total Commander на MacOS. В целом пользоваться можно, и даже местами удобно. Но поиск в нем явно не самая сильная сторона: и встроенный Lister проигрывает в функциональности, и хоткеи работают не так. Пользоваться можно, но снова не то.
IDE. Все хорошо, но медленно. Обычно в IDE открывается один проект или группа репозиториев внутри одного проекта. Даже в относительно легковесной VS Code интерфейс становится перегруженным, а команды начинают работать медленно. Плюс открывать IDE под каждую необходимость поискать что-то не очень оптимально, хочется решение пошустрее.
Классические CLI-утилиты из коробки. При необходимости искать с помощью grep и find, конечно, можно, но лично для меня это никогда не было быстрым и удобным способом что-то найти. Из минусов можно отметить высокие накладные расходы (банально нужно много печатать) и низкую интерактивность. Из плюсов — наличие в любом дистрибутиве.
В итоге вопрос подвис без какого-либо решения. Возможно, и к лучшему, что не стал целенаправленно искать серебряную пулю, ибо за время, пока проблема настоялась, я набрел на несколько весьма интересных инструментов.
Что будем использовать
В последнее время появилось довольно много современных реинкарнаций классических линуксовых утилит. Многие из них пишутся на Rust/Go, что дает кросс-платформенность и многопоточность из коробки. Новые языки привлекают большие сообщества, у проектов много контрибьюторов и тысячи звезд на гитхабе. Отсутствие необходимости соблюдать обратную совместимость развязывает руки в плане оптимизации и построения более функциональных консольных UI. Некоторые из таких утилит получили заслуженное признание в комьюнити, взглянем повнимательнее на несколько из них.
bat. Утилита bat — это современная альтернатива классической команде cat
в Linux, предназначенная для вывода содержимого файлов в консоль. Одним из ключевых преимуществ bat
является подсветка синтаксиса, которая делает чтение кода и конфигов куда более удобным и наглядным. Кроме того, bat
поддерживает нумерацию строк, что упрощает навигацию по файлу и анализ его содержимого. Утилита также интегрируется Git, показывая изменения в файлах. bat
имеет встроенную поддержку для просмотра содержимого нескольких файлов одновременно, а также возможность отображения содержимого в виде страниц. То есть тот же cat
, только функциональнее. На настоящий момент это мой избранный легковесный просмотрщик.
fzf. Утилита fzf — это интерактивный инструмент командной строки для поиска и фильтрации текста. Он принимает на вход список (обычные строки, разделенные символом переноса) и показывает окошко с возможностью нечеткого поиска в этом списке. Так можно быстро находить файлы, команды в истории, процессы и др. Важно заметить, что fzf
не просто хорошо решает свою задачу, а еще и гибко конфигурируется, что позволяет использовать его в комбинации с другими утилитами.
rga. Утилита rga
(ripgrep all) — это расширение утилиты ripgrep
, предназначенное для поиска текста в различных типах файлов, включая архивы и документы. В отличие от стандартного ripgrep
, rga
поддерживает поиск внутри PDF, DOCX, ZIP и т. д. в зависимости от установленных плагинов. Он использует специализированные конверторы, например pandoc
, для извлечения текста из «сложных» форматов файлов. rga
сохраняет высокую скорость работы благодаря эффективному алгоритму поиска (в т. ч. по регулярным выражениям), характерному для ripgrep
.
Собираем все вместе
Впервые способ заставить работать fzf
с rga
попался мне на глаза в заметке:
Концептуально суть заключается в следующем:
fzf
отвечает за отображение легковесного UI (список найденных файлов/строк и превью результатов), а также кастомизацию хоткеев и обработку ввода;rga
выступает в роли поисковика файлов по содержимому, делает "heavy lifting";bat
опционально используется вместо встроенного превью;скрипт в виде функции кладется в
.bashrc`/`.zshrc
, а затем вызывается из терминала по имени.
Не забудьте установить утилиты и перезапустить оболочку после правок.
Вариантов таких сборок нашлось немало, например:
https://github.com/phiresky/ripgrep-all/wiki/fzf-Integration
https://github.com/junegunn/fzf/blob/master/ADVANCED.md#using-fzf-as-interactive-ripgrep-launcher
https://stackoverflow.com/questions/77415675/how-to-use-rga-ripgrep-all-with-fzf-for-searching-the-pdf-file-and-then-using. Один из них даже встроили в
rga
в виде отдельного бинарника.https://github.com/phiresky/ripgrep-all/blob/master/src/bin/rga-fzf.rs. С одной стороны работает из коробки, с другой — почти не кастомизируется.
Идея везде примерно одинаковая, скрипты различаются способом показа результатов поиска (только имена файлов или имена с номерами строк), формированием превью и биндов хоткеев. Изучив несколько вариантов и изрядно наигравшись с комбинацией параметров, я пришел вот к такой версии (репозиторий):
qse() {
RG_PREFIX="rg --files-with-matches"
local file
file="$(
FZF_DEFAULT_COMMAND="$RG_PREFIX '$1'" \
fzf \
--preview="[[ ! -z {} ]] && rg --pretty --context 5 {q} {}" \
--disabled --query "$1" \
--bind "change:reload:sleep 0.1; $RG_PREFIX {q}" \
--bind "f3:execute(bat --paging=always --pager=less --color=always {} < /dev/tty > /dev/tty)" \
--bind "f4:execute(code {})" \
--preview-window="70%:wrap"
)" &&
echo "$file"
}
Разберем ключевые моменты:
В
RG_PREFIX
указывается базовая команда поиска с флагами по умолчанию; я остановился наrg
, потому что поиск по отличным от кода файлам мне требуется гораздо реже.В
FZF_DEFAULT_COMMAND
указывается, чем fzf ищет по умолчанию (вместоfind
).--preview
определяет команду, которая отобразит найденный результат; она будет вызываться каждый раз при навигации по результатам поиска.--disabled
отключает поиск в fzf, превращая утилиту в UI-прослойку.--bind
на событиеchange
перезапускает поиск при вводе.--bind
на клавиши F3 и F4 запускаетbat
и VS Code соответственно.С помощью
$file
выбранный файл выводится в консоль.
Как нетрудно заметить, здесь фактически воссоздан функционал поиска из TC. Работает быстро, плавно и отзывчиво благодаря перезапуску поиска после каждого нажатия (событие change). Есть подсветка найденных результатов и возможность запустить просмотрщик/редактор.
Давайте посмотрим, как работает вживую, на небольшой демонстрации. Специально для нее я подготовил каталог, в который склонировал код Kubernetes, VictoriaMetrics и fzf. Сначала выполняется поиск всех объявлений функций в Go по ключевому слову func
, затем навигация по результатам, точечный просмотр файлов и повторный поиск с более специфичным запросом func TestQueue
. Выглядит вот так:
Почему qse? Хотелось придумать короткий и удобный шорткат, изначально это был qs (quick search, qtros search, whatever). Далее в процессе отладки я наплодил несколько версий в алфавитном порядке: qsa, qsb, ..., qse. Дойдя в экспериментах до версии «e», я получил все желаемое поведение и уже успел немного привыкнуть к имени. Поэтому qse.
За время написания статьи я еще немного покрутил скрипт: заменил превью на более функциональное на основе batgrep
. Эту и последующие правки можно будет найти в репозитории. Вот как это выглядит:
Кстати, пока тестировал свою сборку, обнаружил баг в fzf, связанный с кэшированием результатов. Его уже успели поправить, 27 октября случился релиз 0.56.0. Поэтому при использовании последней версии у вас не должно быть проблем!
Заключение
В статье рассмотрен способ создания специализированного поисковика из подручного опенсорса. Несмотря на простоту и небольшой размер, получившийся скрипт уже вовсю используется в работе, помогая искать диагонально по десяткам репозиториев крупных проектов. Примечательно, что каждый может без особого труда докрутить скрипт под себя: выбрать альтернативный просмотрщик, поставить другие хоткеи, искать не только по файлам и т. д. Надеюсь, что опыт из статьи оказался полезным и что вы нашли в ней что-то новое!
Вопросы для обсуждения в комментариях:
Стоит ли собрать инсталлятор такого поисковика, чтобы облегчить установку, и как это лучше сделать?
Пора ли добавлять современные CLI-утилиты в дистрибутивы операционных систем, чтобы ими можно было пользоваться "из коробки" на любой машине?
Комментарии (12)
sirojiddin13
19.11.2024 17:30Не хочу быть тем самым челом, но ведь это уже есть во встроенном поиске в JetBrains IntelliJ IDEA? Если хочется по нескольким проектам искать, можно просто workspaces подрубить.
thethee
19.11.2024 17:30Не на всех рабочих станциях или удалённых серверах можно запустить JetBrains IntelliJ IDEA. Согласен, в статье речь про MacOS, но предложенные утилиты прекрасно работают и на Linux системах.
sirojiddin13
19.11.2024 17:30Ну для удаленной разработки уже пилится тулза https://www.jetbrains.com/remote-development/ , правда сервер весит почти столько же как сама ide, в этом плане vscode по легче будет
QtRoS Автор
19.11.2024 17:30Соглашусь, что IDE это отличный вариант, который просто работает и в среднем не заставляет задумываться об использовании дополнительных инструментов. Если же по той или иной причине есть необходимость в каком-то из следующих аспектов:
Практически нулевая скорость запуска и затраты оперативной памяти
Компактный консольный UI, который можно запустить в терминале
Приближенная к максимальному скорость поиска
Возможность кастомизировать и скриптовать действия
Минимализм "дистрибутива"
Свободная лицензия и бесплатность
То можно посмотреть в сторону рассмотренного поисковика.
gmelikov
19.11.2024 17:30Кстати это стандартная боль в IDEшках - когда у тебя не 1 проект а больше 10 (я держу около 100, к примеру). Обычно для IDE рекомендую заводить отдельный воркспейс и накидывать вручную все проекты в него (ибо они не всегда лежат в одной директории), тогда можно будет искать по всем проектам разом.
Лично я юзаю vs code и вместо поиска по всем проектам поставил Git Project Manager , с которым могу за 1 хоткей быстро переходить в нужный проект без надобности заводить воркспейс и тд. Когда очень припрёт поискать везде - классический grep.
Если кто знает более удобные пути наконфигурить для глобального поиска тот же vs code - поделитесь, спасибо!
shaggyone
19.11.2024 17:30Есть люди которые вообще не пользуются IDE, предпочитают им условный vim. Потом, автор указал позицию по поводу IDE.
31415
19.11.2024 17:30Итак, на старте мы имеем MacOS + желание быстро и удобно искать текст.
берем far2l и закрываем статью
QtRoS Автор
19.11.2024 17:30Постараюсь позднее добавить FAR к сравнению. Честно признаться, думал он исключительно под linux работает, поэтому рассмотрел только MC из консольных файловых менеджеров.
Буду признателен, если поделитесь своим опытом: как-то кастомизируете или берете вариант "из коробки", какие просмотрщик/редактор используете и настраивается ли это, во всех ли сценариях ищете в фаре или дополняете другими инструментами.
31415
19.11.2024 17:30Никак особенно не настраиваю, меня и так устраивает. Основной сценарий использования поиска - как у вас написано - в farl2 полностью идентичен.
Но поиск в Far-e (в любом) не отличается высокой скоростью, поэтому это подходит только для небольших объемов - до гигабайта. Если нужно грепать много гигабайтов, то использую ripgrep.
mapcuk
19.11.2024 17:30Тестил разные штуки. Вместе с IDE у меня всегда открыта консоль. В ходе работы над кодом и в целом обработки текстовых файлов больше всего зашёл silver searcher (порой в конвейере с cut, grep), а вот fzf и ripgrep как-то не прижились
ag --ignore *_test.go --go -C 2 Seed ./pkg | less -S
В остальном "попрыгать" по коду и посмотреть удобнее в IDE.
Иногда люблю почитать сниппеты на проекте commandlinefu.com - иногда попадаются "бриллианты".
VADemon
19.11.2024 17:30Классно, заберу. Как-то раз тоже скриптовал rg, чтобы мне редактор на нужной строке открывал для вхождений.
danielsedoff
Вот такое интересно будет попробовать. Спасибо:)