f4 0.1.1-alpha: первый публичный релиз асинхронного клона Far Manager на Go

Страницы:  1

Ответить
 

Professor Seleznov


Привет, Хабр! Если вы читаете мои дайджесты, то знаете, что обычно я пишу о развитии проекта far2l — порта Far Manager под Linux, macOS и BSD. Но сегодня случай особый. На прошлых выходных я обещал вам рассказать про f4 — написанный с нуля клон far2l на языке Go.
Сегодня состоялся релиз первой публичной альфа-версии 0.1.1-alpha. В этой статье я расскажу, как я пришел к идее переписать легендарный файловый менеджер, почему выбрал Go, как в этом помогли нейросети и почему современному консольному приложению не обязательно страдать от «наследия предков».
pic
Диалог копирования f4. Выпадающее меню позволяет выбрать, копировать отдельым потоком или добавлять задачу в общую очередь файловых операций с оптимизацией под многопоточность.
Сразу спойлер: что под капотом f4?
Прежде чем углубляться в историю, давайте сразу обозначим, ради чего всё это затевалось. Чем f4 принципиально отличается от своего прародителя?
  • Тотальная асинхронность. Пользовательский интерфейс больше не «фризится» при чтении директории по медленному SFTP. Вы можете запустить тяжелую операцию и продолжать работу в панелях. Есть и выстраивание операций в очередь с управленим числом потоков на ресурс для максимальной эффективностью IO, как в менеджерах загрузок.
  • Out-of-process плагины (RPC). Никакой боли с бинарной совместимостью и C++ ABI. Плагины общаются с ядром через stdin/stdout по протоколу MessagePack. Пишите плагины на Python, Rust, Node.js или Lua — если язык умеет писать в консоль, он может расширять f4. Экосистема превыше всего!
  • FISH+ (WIP). Эволюция удаленного управления файлами. Вместо скачивания файла для поиска по нему, f4 будет слать команду на сервер, чтобы он нашёл сам. Это не только спасает на медленных линках, но и решает проблему WSL: можно запустить f4 в Linux-контейнере и управлять файлами хостовой Windows с нативной скоростью!
  • Собственный UI-фреймворк (vtui). Написанная с нуля библиотека компонентов в духе классического Turbo Vision, но изначально асинхронный, с поддержкой современных протоколов ввода (kitty keyboard, win32-input-mode) и Zero-allocation рендерингом.
pic
Демонстрационное приложение фреймворка vtui, написанного специально для f4.
Как я пришёл к переписыванию Far'а
Работая над far2l, я часто ловил себя на мысли, что узкое место разработки консольных файловых менеджеров — это не процессорные такты, а ввод и вывод. И поэтому ручное управление памятью в C/C++ скорее отнимает ресурсы разработчика, которые могли бы уйти на оптимизацию I/O, чем приносит пользу за счёт собственной эффективности. И, конечно, очень сильно ограничивала развитие синхронная природа старого кода: фоновое копирование в far2l на самом деле есть, но из-за синхронного ядра оно реализовано так, что вы скорее всего даже не знаете, что оно там есть, и как его найти.
Когда-то давно, только начав изучать программирование, я после BASIC'а и Turbo Pascal'я сразу прыгнул в Ассемблер. Долго не мог понять: зачем вообще нужен промежуточный язык между Паскалем и ассемблером, который даёт бинарник той же эффективности что и Паскаль, но при этом предоставляет куда больше способов прострелить себе ногу, включая самые изощрённые. Со временем, конечно, я в той или иной степени смирился (думаю, как многие из нас), но интуитивное чувство, что интерфейс должен писаться проще, меня не покидало. И в особенности потому что я избалован высокоуровневыми языками как php, и мне не очень интересно тратить время на отслеживание утечек памяти в коде отрисовки выпадающего меню.
Кстати, оригинальный Far 2 поддерживал плагины и а Pascal/Delphi, и это было круто (и у меня как-то даже получилось завести это и в far2l). Но Pascal сейчас не в моде, а разработчиков, знакомых с ним — мало (к слову, на мой взгляд, причина провала — то, что дефолтные строки не в UTF8 и только это).
Были и другие проблемы, фундаментально неразрешимы на базе существующего кода:
Зависимости и дистрибуция. Ещё одна боль. Мы слишком долго ждали, пока far2l возьмут в Debian, и так и не дождались пакета в официальной Fedora. Зависимость от версий libc, wxWidgets, glib — всё это делало дистрибуцию сложной. Я хотел, чтобы файловый менеджер в Linux работал как Telegram, Tor Browser или оригинальный Far в Windows: скачал один бинарник, положил куда угодно, и он просто работает, независимо от того, Ubuntu у тебя, Arch, старый CentOS или вообще Gentoo. А ведь ещё есть плагины! Сейчас пользователю плагина надо самому подсовывать его исходники в дерево far2l, куда такое годится?
pic
Плагины, включенные в состав far2l. О существовании других никто не знает, потому что нет организованной системы дистрибьюции.
Плагины и экосистема. Сам я особо не пользовался плагинами Far не «из коробки» на винде — всего хватало и так, но всегда понимал их важность. При этом заставлять людей учить отдельный язык, Lua, только ради написания плагина — на мой взгляд, утопия. И тут я вспомнил гениальную мысль создателя OpenStreetMap (OSM): «Дайте людям простой инструмент здесь и сейчас, и занимайтесь сообществом» (они там годами шлифовали идеальную модель данных при нуле контрибьютеров, а он дал людям точку, линию и полигон, с возможностью привязывать к ним пары ключ-значение, и всё поехало). Хотелось дать людям универсальный штепсель, куда можно будет цепляться хоть из Питона, хоть из бинарного кода. Таким штепселем стал бинарный RPC интерфейс через stdin/stdout.
Обратное портирование и ИИ. Наконец, у меня была давняя мечта — портировать far2l обратно на Windows (звучит как шутка, вообще-то получить единую базу кода для всех платформ было бы очень неплохо, и это не что-то неосуществимое, в cygwin мы даже когда-то работали). Проблема в том, что внутри far2l есть прослойка WinPort, которая эмулирует (частично подмножество, а частично надмножество) Windows API под Linux. Когда я пробовал натравливать на этот код нейросети для обратного портирования, они сходили с ума, путаясь между WinPort и реальным WinAPI, куда я пытался WinPort транслировать. С человеком, скорее всего, случилось бы то же самое.
Одновременно с этим главный мейнтейнер far2l как-то обмолвился о зашкаливающей цикломатической сложности некоторых старых участков кода. Прекрасно помню, чего стоило добавить в редактор поддержку переноса по словам. Сказать, что этот код запутан, значит, ничего не сказать! И я подумал: а не проще ли уже начать понемногу параллельно переписывать это всё с нуля, сразу правильно? С асинхронностью, отдельным удобным UI-фреймворком и на современном языке.
Почему именно Go?
Итак, было принято решение разделить проект на две части: мощную UI-библиотеку (по смыслу — асинхронного наследника Turbo Vision) и сам файловый менеджер на её основе. Перебрал несколько вариантов:
  • Rust: Да, он быстрый. Но цена владения (borrow checker) при написании UI-деревьев с перекрестными ссылками (фокус, паренты, линки) слишком высока. Нейросети в нём тоже иногда откровенно плывут. Код UI не должен быть сложным в разработке и генерации.
  • Node.js / Python: Была безумная мысль написать или взять готовый плюсовый UI движок (как современный форк того же Turbo Vision) и сделать к нему биндинги из JS или Python. Но биндинги — это всегда хрупко. А ещё Python отпал из-за GIL (прощай, нормальная асинхронность) и кошмара с дистрибуцией (pip, например, на современных Ubuntu ругается на externally managed среду, и это не даёт установить хоть что-нибудь сложнее тривиального). Тащить с каждым плагином свой VirtualEnv? Нет, спасибо. К тому же, для тулзы, которая открывается по 100 раз на дню, время старта скриптовых виртуальных машин слишком велико.
Go подошел идеально. Он компилируется в статически слинкованный бинарник, решая проблему дистрибуции. У него есть сборщик мусора, так что мне не придётся тратить время на отлов сегфолтов в UI. У Go мощнейшая встроенная поддержка многопоточности, горутины и каналы. За ним стоит огромная корпорация и развитая экосистема, так что мне не пришлось писать с нуля алгоритмы ZIP, TAR или крипотграфию для SFTP. И современные нейросети пишут на Go на очень приличном уровне!
Архитектура и магия скорости
Бытует заблуждение, что Go — медленный язык из-за сборщика мусора. Это просто не так: ограничение не в языке, а в стиле разработки. Уже сейчас, в состоянии альфы, f4 субъективно ощущается отзывчивее, чем far2l на C++.
В чём магия? Я исходил всё из той же идеи, что узкое место консольного файлового менеджера — это ввод-вывод, а не отрисовка кнопок. Поэтому как раз ввод-вывод в f4 оптимизирован маниакально. Например,
  • Zero-allocation рендеринг: Фреймворк vtui использует систему двойной буферизации. При вызове Flush() он побитово сравнивает логический экран с теневым буфером терминала и генерирует минимальные ANSI-последовательности для отрисовки только изменений, без единой аллокации памяти в куче. Сборщик мусора просто отдыхает, пока вы быстро скроллите список файлов.
  • PieceTable в Редакторе: Вместо загрузки файла в память целиком, редактор f4 использует структуру данных Piece Table, натравленную на асинхронный VFS-буфер. В результате f4 открывает гигабайтные логи по SSH мгновенно, не выедая ОЗУ, и позволяет мгновенно прыгать в конец файла.
  • Event Draining: Обработка ввода (например, вставку больших кусков текста в терминал в режиме Bracketed Paste) собирается в пакеты, и экран перерисовывается только один раз в конце.
pic
Редактор прекрсно справляется со случайным 80-мегабитным бинарником.
В скомпилированном виде без отладочной информации f4 весит около 15 Мб и жмется UPX'ом до 4-5. По меркам 2025 года это ничто, субъективно запускается он мгновенно.
Минусы
15 Мб отлично для десктопа или виртуалки, но даже 5 многовато для копеечного роутера с 16 Мб флеша, там far2l или mc чувствуют себя лучше. Эту проблему можно обойти тремя способами:
  • Выкинуть зависимости. Большую часть веса в f4 дают библиотеки криптографии (SSH/SFTP) и архиваторы. Если сделать им легковесные аналоги, где вместо встроенных библиотек плагины используются обертки над внешними утилитами (ziptarssh и т.д.), бинарник похудеет до 5-6 Мб, что уже будет сопоставимо с весом конкурентов.
  • Использовать FISH+. Зачем вообще тащить тяжелый комбайн на роутер? Запускаем f4 на рабочей машине, а к роутеру цепляемся по SSH. Согласно протоколу FISH+ на роутер закидывается крошечный шелл-скрипт (или микробинарник), который берет на себя рекурсивный подсчет размеров, поиск файлов сервером, парсинг директорий и т.д. — всё то, что удаленно делать долго, будет делаться на стороне SSH сервера. Это же решает и проблему WSL — работая в f4 внутри виртуалки, вы сможете мгновенно и без тормозов управлять файлами хостовой Windows, если там есть OpenSSH сервер, который давно ставится одним кликом.
  • А почему бы не допилить mc? Можно сделать (и я подумываю как-нибудь заняться этим) форк Midnight Commander, добавив туда привычные фаровские шорткаты (хотя бы по галке, но вообще-то, пользователей Far на Windows в мире всё же несоизмеримо больше) и поддержку современных терминальных протоколов (kitty, win32im, osc52, bracketed paste). Это закроет нишу embedded окончательно.
pic
mc, работающий во встроенном терминале f4. Добавить передачу всех сочетаний клавиш и буфера обмена между ними — и станет хорошо!
f4 и far2l
Сразу отвечу на уже интересовавших многих вопрос: я не отказываюсь от разработки far2l полностью в пользу f4. Буквально недавно занимался для far2l тестами переноса по словам в редакторе.
f4 — это экспериментальный полигон. Здесь можно пробовать то, что долго тащить в legacy C++ код или когда страшно что-нибудь сломать. У f4 параноидальное покрытие тестами — экспериментируйте на здоровье, что-то здесь сломать будет очень непросто! А если фича приживётся, её можно будет перенести и в far2l. Например, реализованную в f4 идею навигации по элементам диалога только с помощью обычных стрелок (а не только Tab) уже просили перенести в старшего брата.
И, конечно, f4 полностью понимает far2l terminal extensions. Он идеально работает внутри встроенного терминала far2l (с общим буфером обмена и всеми хоткеями), и наоборот — far2l прекрасно работает в терминале f4.
Более того, архитектура f4 вовсю заимствует самые удачные идеи своего предка. Например, как и в far2l, в f4 реализован нативный графический бэкенд для X11. Да, консольный файловый менеджер на Go может рисовать себя напрямую в иксовое окно, минуя эмуляторы терминала. Зачем? Потому что терминалы обожают перехватывать нужные нам сочетания клавиш.
pic
При отрисовке в иксы символы рисования рамок приходится рендерить кастомно, чтоб не было дырок в рамках из-за сглаживания шрифтов. Для части символов кастомную отрисовку ещё предстоит добавить.
Приглашаю к тестированию!
Проект находится в стадии alpha. Базовый функционал уже работает стабильно: панели, архивы (через встроенный плагин), SFTP/FTP (тоже встроенный плагин NetFox), редактор, вьювер, встроенный терминал с поддержкой PTY.
Если вы любите консоль, соскучились по Фару или просто хотите посмотреть, на что способны современные TUI на Go — добро пожаловать!
Скачать готовые бинарники для Windows, macOS и Linux (включая ARM) можно прямо на главной странице репозитория:
https://github.com/unxed/f4
Буду рад вашим звездочкам, баг-репортам и идеям в Issues. А если вы знаете Go или хотите написать свой RPC-плагин — в репозитории есть готовый SDK и пример dummy_rpc. И, пожалуйста, поделитесь опытом знакомства в f4 в комментариях!
Есть и пасхалочка -Источник
 
Loading...
Error