Как далеко уводят мечты об идеальном «поиске по файлам» +9


Часто ли вы запускаете поиск по текстовым файлам? Я — каждый день уже больше 25 лет.


Мои задачи очень разные по сложности и объему.


Во-первых, как программисту, мне, конечно, нужны поиски в кодах. Это задачи простые (наборы папок и файлов малы) и быстрые (результаты появляются почти сразу).


Во-вторых, как оператору, мне приходится искать по сотням (иногда тысячам) папок среди тысяч (иногда сотен тысяч) файлов. Это задачи тяжелые и по объемам результатов, и по времени их получения. Обычно результаты таких поисков еще требуют дальнейшей ручной или программной обработки.


Вся работа происходит в Windows.


Расскажу куда завело меня желание иметь подходящий инструмент для таких задач.


В начале был TextPad


Больше 10 лет TextPad был моим основным легким инструментом.
Тогда (до и немного после 2000) он прекрасно справлялся.


Поиск по файлам выглядел так (TextPad, 2004)


TextPad_fif


Впечатления

(+) В диалоге мало настроек, поэтому он не заслоняет содержимое файлов.
(+) Вкладка с результатами поиска имеет спец.поведение — Ввод/ДвКлик позволяют открыть найденный фрагмент. В остальном это обычный текст "в памяти" редактора.
(+) Есть настройка File counts only для поиска не самих фрагментов, а только их количества по файлам. Это заметно ускоряет предварительный поиск.
(-) В диалоге мало настроек и мало команд. Окно диалога фиксированного размера.
(-) Вычурный язык регулярных выражений, например, вместо принятого сейчас \w нужно было писать [:word:].
В целом тут качественно, но скромно.




Шатания и сравнения


Ограниченность TextPad и его заморозка вели к постоянным пробам других редакторов.
Теперь уже не вспомнить все перепробованные, но среди них были:


  • MS Visual Studio. Тяжелый монстр.
  • Eclipse. Тоже монстр.
  • IntelliJ IDEA. Очаровательный. Служил и служит поставщиком примеров того, как можно сделать удобно.


    Впечатления

    Тот старый IDEA уже утерян. Для сравнения текущая реализация в PyCharm, 2017.1.3
    PyCharm_fif
    (+) Невообразимое число настроек и режимов.
    (+) Самодостаточность диалога: видны и результаты, и исходные тексты, и статистика поиска.
    (-) Нет ограничения на глубину поиска по папкам — либо одна, либо сразу все.
    (-) Нет (не знаю способа) поиска по нескольким папкам.
    (-) Нет (не знаю способа) вывода результатов в текстовом формате.


    В целом это мощно, но не универсально.




  • Notepad++. Не помню, что помешало на него перейти.
  • SublimeText3, 2013. Хакерский инструмент — отличная работа, минимум средств, но требуется много держать в голове.


    Впечатления

    Sublime_fif
    (+) Минимум контролов.
    (-) Разные настройки "где искать" все в одном поле. Нужно учитывать их взаимодействие.
    (-) Только прикрепленный диалог.
    (+) Результаты сразу попадают в текстовую вкладку, и они наглядные.
    (-) Результаты фиксированного формата: полный путь и имя файла, потом найденные в нем строки. От локализации найденных фрагментов есть только номера строк.
    (+) Есть настройка "Контекст", чтобы в Результаты попадали найденные строки вместе с соседями.






Есть такой парень


Взяв за основу принцип "Нет идеального инструмента? Создавай", изменил направление усилий.
Стал обращать внимание на отзывчивость разработчиков. И в 2012 году мне повезло с SynWrite.


Единственный разработчик, Алексей Торгашин, принимал большинство идей, делал быстро и качественно. Типичное время реализации — день-два. На форуме попросил его о доделке, потом еще об одной, потом еще о десяти, и еще… Где-то третьем десятке понял, что мы удачно встретились.


С моей подачи Алексей довел поиск по файлам до такого состояния (SynWrite, 2016)


image


Впечатления

(+) Есть извлечение папки из текущего файла Current folder.
(+) Можно сортировать файлы по дате модификации.
(+) Есть список готовых наборов для поиска preset и быстрый доступ к ним (F3).
(-) Диалог очень перегружен и занимает много места.
(-) Результаты оформлены в виде контрола дерево в нижней панели. Чтобы получить их в текстовом виде нужно вызывать команду из локального меню.




"Чего тебе надобно, старче?"


С SynWrite искать стало значительно удобнее. Как известно аппетит приходит во время еды, поэтому мои хотелки все множились. Вот только отношение к ним у разработчика, моей золотой рыбки, было все более критическим — "Почернело синее море".


Вот что хотелось получить


  1. Обязательно нужны результаты сразу в текстовом виде (для дальнейшей обработки).
  2. Результаты должны быть самодостаточными! Чтобы их можно было сохранять, закрывать, открывать — и они оставались свежими, то есть готовыми к работе.
  3. Нужен поиск в файлах на диске и в открытых (измененных) файлах.
  4. Диалог при решении простых задач должен быть компактным. В частности контролы для "замен" и редких настроек не должны мешать при обычном поиске.
  5. Обязательно нужна опция для результатов вместе с соседними строками.
  6. Обязательно нужен режим "полное описание одного результата в одной строке" (для дальнейшей обработки).
  7. Справочная информация о том, как заполнять поля, нужна не во внешнем файле или облачной странице, а прямо тут же, например, в тултипах.



Сделай сам!


Мне повезло еще раз. Алексей создал новый редактор CudaText и прикрутил к нему Python API.


Наконец-то, можно было сделать все как хочется в виде плагина на Питоне.


Первая версия вышла в мае 2016.


Минимальный набор контролов

fif-dlg-min


Максимальный набор контролов

fif-full-dlg
Это, конечно, не совсем первый блин, но желание "все запихать в пирогдиалог" явно просматривается.


Вот что я применяю сейчас


cuda_fif


Реализованы все хотелки из списка "Чего тебе".


(1) Результаты можно видеть либо внутри диалога, либо сразу выводить в файл (если [x] Send).


(2) Результаты самоописательные. В тексте


fif_rpt_info


достаточно информации, что узнать полное имя файла и место найденного фрагмента.
1:21:6 означает: 1 строка, 21 колонка, длина фрагмента 6.


Плагин умеет эту информацию извлекать и использовать.


(3) Есть поиск по несохраненным документам. Для этого в поле In folder вводится специальная строка <Open Files> (работают сокращения <Tabs> и <t>).


(4) У диалога можно скрывать контролы, нужные для задания исключенных файлов, для выполнения замен, для показа результатов и исходников.


Скрытые контролы

• Минимальный набор fif_min
• Минимальный набор и замены fif_repl
• Минимальный набор и задание исключений для файлов fif_excl


(7) В тултипах у подписей к свободно заполняемым полям есть пояснения


Подсказки

fif_incl
fif_tip_fold
Более подробная информация доступна при вызове Alt+H
fif_help_tips


Реализовано еще много разных штук.


  • Глубина обхода может быть нулевая (Only), полная (+All), либо от 1 (+1 level) до 5 (+5 levels) уровней.


  • Редко применяемые параметры поиска появляются по For search



fif_extra_fi


Доп.параметры поиска
• Можно пропускать поиск в *скрытых* и/или *двоичных* файлах.
• Можно предварительно отсортировать файлы по дате.
• Можно остановить поиск после нахождения заданного числа фрагментов и/или файлов.
![fif_first](https://user-images.githubusercontent.com/7419630/42456464-b0448f20-839d-11e8-8876-435fdfead1ad.png)
• Можно указать кодировки, которые будут применяться при попытках прочитать файлы (до первой успешной).

  • Если результаты будут появляться не внутри диалога ([x] Send), то для них есть дополнительные параметры

fif_extra_rp


Доп.параметры результатов
• `Report to` - выводить в новый таб, в последний использованный таб, в файл с конкретным именем и открыть его.
• `Append` - дополнять предыдущие результаты.
• `Tree type` - можно по разному разбивать строки 
`<path><file><r:c:l><line>`, то есть `<путь><файл><место><строка с фрагментом>`
в частности, вообще не разбивать ("Чего тебе"-6). Возможные варианты описаны в Справке
![fif_help_tree](https://user-images.githubusercontent.com/7419630/42456892-a7d867c0-839e-11e8-87f2-2b9ce6b5c530.png)
• `Align (r:c:l)` - дополнять пробелами данные из `<r:c:l>` так, чтобы все `<строки с фрагментом>` начинались с общей колонки.
Пример: ![fif_align](https://user-images.githubusercontent.com/7419630/42616450-036e3ae6-85b7-11e8-861a-cf27a3a07c7a.png)
• `Add context` - дополнять результаты соседними строками ("Чего тебе"-5).

  • Можно запоминать параметры поиска — делать пресеты. Первые пять пресетов применяются по Ctrl+1(..5), остальные — из меню или диалога Presets


    диалог Presets

    fif_pset




  • Можно запоминать место/размеры диалога/контролов — делать Layout. Первые пять Layout применяются по Alt+1(..5), остальные — из меню.


  • Почти все в диалоге можно сделать без мышки. Для этого есть


    Хоткеи

    fif_help_keys




  • Для команд есть


    Меню

    Кнопка с гамбургером — это просто "=" с подчеркиванием, поэтому она доступна по Alt+=.
    fif_menu_find




  • Процесс поиска разбит на три последовательные фазы:
    • формируется коллекция подходящих файлов,
    • из файлов извлекаются фрагменты,
    • заполняется отчет о результатах.
    Информация о каждой фазе и их смене отображается в статус-контроле.
    Esc позволяет прервать работу только текущей фазы и перейти к следующей с уже накопленными данными.


  • Кроме задаваемых пользователем обычных параметров есть много редко изменяемых опций. Сейчас их 19 и для них отдельный диалог


    диалог Options

    fif_opts
    Например, среди опций есть такие:
    • Использовать выделенный текст для заполнения поля Find what при открытии диалога.
    • Прерывать одним ESC все фазы поиска.
    • Закрывать диалог при удачном поиске и выводе результатов в файл.
    • Сворачивать предыдущие результаты при дописывании новых в тот же файл.
    • Сохранять все параметры поиска в первой строке результатов. Это позволяет позже загрузить их в диалог.
    • Пропускать файлы с размером больше заданного.
    • Задать стиль для найденных фрагментов. Доступны цвет/жирность/наклонность текста, цвет фона, стиль рамки с каждой стороны. На снимках выше установлен стиль "рамка точками под фрагментом".




  • Часть команд плагина работают до и после диалога.

cuda_menu


Из главного меню CudaText
• `Find in *` - вызывать диалог с готовыми параметрами для разных поисков.
• `Navigate to *` - прыжки из результатов в исходники, с разными размещениями места, где открыть исходник.
• `Jump to *` - прыжки к следующему/предыдущему фрагменту этого же исходника, к фрагменту в следующем исходнике, к фрагменту в следующей папке.
• `Configure *` - какой прыжок делать при двойном клике в результатах, разные прыжки при разных зажатых модификаторах
![fif_dclk](https://user-images.githubusercontent.com/7419630/42460070-aa1c5f26-83a5-11e8-9103-11f676dcf08a.png)



CudaText и Python


Несколько слов о платформе, на которой вырос мой плагин.


Алексей Торгашин сделал SynWrite на Дельфи. Половина кодов этого редактора были лицензированы у другого разработчика, и это мешало внедрению новых идей. А еще Дельфи платный. Из-за этого Алексей перешел на Free Pascal и IDE Lazarus, реализовал недостающие части самостоятельно и создал в 2015 году CudaText, а SynWrite заморозил. Возможность начать с чистого листа была использована толково — он сделал несколько сильных проектных улучшений.


  1. Настройки стали накладываться слоями: умолчание, общие, лексовые, текущего файла. Они стали храниться в json, а не в ini. Изменение настроек стало обычным редактированием текста в формате json.
  2. Ядро избавилось от огромного множества некритичных сервисов. В частности, от поиска по файлам, макросов, вызова внешних утилит, конфигуратора меню, сниппетов, сортировок, фаворитов и пр.
  3. Для создания таких сервисов ядро предоставляет Python API, в которое входит GUI библиотека. Сейчас плагинами реализованы все прежние сервисы и добавлена масса новых.
  4. Кроме того ядро само стало многомодульным. Появились компоненты маркеров, букмарков, атрибутов для скобок и пр.

Здесь хорошо видно влияние Sublime Text. Насколько я знаю, Алексей не скрывает, что учитывает идеи из Sublime и его плагинов. На Atom и Brackets он тоже поглядывает.


Получилось само по себе хорошо, а с учетом высокой продуктивности и отзывчивости Алексея — отлично. Чтобы пояснить о какой продуктивности и отзывчивости идет речь, приведу несколько фактов:


  • Алексеем создано: лексеров — 201, линтеров — 34, сниппет-наборов — 12, плагинов — 93.
  • С октября 2015 года было более 1200 пожеланий (не претензий) на ГитХабе, из которых более 90% удовлетворены.

CudaText свободный, с открытыми исходниками, есть сборки для Win/Linux/Mac/BSD. Сам я пользуюсь только Win-версией, но вижу пожелания и претензии от пользователей с Linux и Mac. На этом же Лазарусе написан, кстати, Total Commander.


Интересно сравнивать Python API у Sublime и у CudaText. Они совсем разные.


• У Sublime — объектный стиль типа DOM.


• У CudaText — процедурный стиль типа API OS, например, WIN32.


Видимо это различие происходит от языков реализации. Если редактор сам реализован на Питон, то DOM стиль API для него естественный — можно хранить ссылки на объекты. А если редактор на Паскале, то хранить он может только handler-ы.


• У Sublime API нет GUI. Плагин, видимо, может использовать Python GUI (Tk? WxPython? Qt?), но стилистика будет контрастировать с Sublime.


• CudaText предоставляет для плагинов GUI в общей стилистике приложения. Во-первых, есть богатый арсенал базовых контролов: checklistbox, listview, checklistview, treeview и, конечно, обычные кнопки, чеки, одно- и много-строчные редакторы, комбобоксы и пр. Во-вторых, есть возможность встраивать компоненты CudaText в GUI. На снимках выше это статус-контрол, локальное меню и редактор-контролы с результатами/исходниками. Такого взаимодополнения чистым Python GUI вообще не достичь.


И без GUI живут

Поскольку я уже создал десяток плагинов для CudaText, и большинство из них общались с пользователем с помощью диалоговых окон, мне теперь крайне сложно представить, как можно создавать плагины без этого богатства. Конечно, если у плагина только один диалог, который позволяет задавать настройки, то редактирование json дает адекватную замену. Но такое редактирование выполняется "до запуска" плагина, оно не может помочь, если нужна живая runtime реакция пользователя.




Есть ли предел?


Примерно раз в два-три месяца, а длится это, напомню, уже два с половиной года, мне кажется, что "это финал" — диалог вылизан, результаты показаны всеми способами, все полезные команды есть. Но поскольку этот инструмент постоянно в работе, поскольку применяются практически все его штучки, то пока руки кнопят очередной поиск или изучают очередные результаты, голова ищет новые улучшения. И находит!


Есть новые идеи

Например, за время написания этого поста родились такие:
• Можно выделить текст в контролах Результаты или Исходник и дать команду на его поиск.
• Можно перехватывать события "открыт файл" с результатами и восстанавливать стиль найденных фрагментов.


Кроме того, мне интересно понять — может ли коллективный разум подсказать новые ходы в этой игре. Поэтому завершаю вопросом к коллегам программистам:
Что еще полезного можно добавить в "поиск по файлам" внутри текстового редактора?




Технические детали реализации плагина к CudaText

• Язык: Python 3.5
• GUI библиотека: CudaText API
• Разработчики: Андрей Квичанский
• Объем кода: >300 Кб
• Число ToDo для плагина: >250
• Число bug/wish для CudaText API: >150
• Форум плагина: GitHub issues


Как попробовать

• Скачать сборку CudaText под свою ОС, распаковать или установить.
• Запустить CudaText и вызвать команду меню Plugins/Addons Manager/Install...
• Ввести "find", чтобы фильтр оставиль только FindInFile, установить (может быть потребуется перевызов CudaText, на Win не нужно).
• Вызвать команду меню Plugins/Find in Files/Find in files...




К сожалению, не доступен сервер mySQL