Редактор acme: пишем почтовый клиент

[std.hugeping] Wed Oct 13 16:46:59 UTC 2021 @hugeping -> All

Медленно, но верно, редактор acme становится моим основным редактором-средой в Linux. Почему это происходит, вопрос отдельный и он не сводится к утилитарности. Проще, удобнее, быстрее -- это всё категории, которые в большей степени определяются нашими привычками. А мной в IT движет любопытство и тяга к простоте. Идея acme очень простая, но при этом мощная. Это не редактор, это прослойка между Unix средой и человеком. Когда вы работаете с acme, "редактором" становится вся ОС. В начале это очень непривычно, но потом -- затягивает. До последнего момента в качестве основного редактора я пользовался emacs и пользовался им как средой. То-есть, кроме собственно редактирования файлов я читал в нём почту (mu4e), общался в телеграм (telega.el), читал pdf ну и так далее... Отвыкнуть от emacs очень сложно, а мне было интересно проводить в acme больше времени, поэтому я решил попробовать перенести в него работу с почтой. Вероятно, можно было бы завести upas, который есть в составе plan9port, но мне этот вариант не очень подходит. Потому что я параноик. Так как почтовые серверы мне не принадлежат у меня есть непреодолимое желание хранить копию своих почтовых ящиков локально на диске. Кроме того, это даёт возможность быстро искать нужное письмо. Поэтому я пошёл другим путём. # Синхронизация почты mbsync Для синхронизации почты между Maildir на диске и imap на сервере нашлась отличная штука: isync (или mbsync). Замечательна она тем, что синхронизация работает в обе стороны. То-есть, удаляя письмо в Maildir вы тем самым удаляете его в imap mailbox. Ну и так далее. Таким образом, вы получаете единый срез почты на многих машинах и всё это прекрасным образом синхронизируется через imap. Конфигурация выглядит примерно так: ==== IMAPAccount gmail Host imap.gmail.com User user@gmail.com Pass password SSLType IMAPS CertificateFile /etc/ssl/certs/ca-certificates.crt IMAPStore gmail-remote UseUTF8Mailboxes yes Account gmail MaildirStore gmail-local SubFolders Verbatim Path ~/Mail/gmail/ Inbox ~/Mail/gmail/Inbox Channel gmail Far :gmail-remote: Near :gmail-local: Patterns * ![Gmail]* "[Gmail]/Sent Mail" "[Gmail]/9front" Create Both Expunge Both SyncState * ==== Правда, mbsync из апстрима создаёт каталоги на диске в кодировке UTF-7. Но в aur есть пакет с поддержкой UTF-8. Обратите внимание на UseUTF8Mailboxes в конфиге. Для других Linux можно собрать версию отсюда: https://sourceforge.net/u/shashurup/isync/ci/utf8-mailboxes/tree/ После того, как всё настроили, можно поставить задачу на таймер в systemd или cron и всё. # Индексация Не так давно я открыл для себя mu. Mu позволяет индексировать почту в Maildir и дальше делать выборку, показ писем, распаковку аттачей и так далее. И всё это очень быстро. Вместе с mu идёт почтовый клиент для emacs -- mu4e. Для создания базы просто делаем что-то вроде: $ mu init --my-address='ваш емейл' И потом периодически делаем индексацию: $ mu index # Идея почтового клиента на acme Mu -- это почти полноценный клиент, по крайней мере, для чтения почты. Практически всё можно сделать из командной строки. Например, вывести последние сообщения: ==== $ mu find --sortfield d --reverse "" | head -n10 ==== Чтобы просмотреть сообщение, вы должны указать путь к конкретному файлу в Maildir. Например: ==== mu view `mu find "" --sortfield d --reverse -f l | head -n1` ==== Конечно, пользоваться в таком виде почтой малореально, но возможностей для скриптования -- масса. И когда я это понял, то решил написать свой фронтенд к mu для acme. # Приложение на acme Acme с помощью файловой системы предоставляет доступ к некоторым функциям по работе с своими окнами, которых оказывается достаточно для написания "приложений". Если вы работаете в Plan9, то файловая система доступна всегда. Если же вы запускаете acme в рамках plan9port, то для доступа к ней можно: 1. Использовать утилиту 9p 2. Подмонтировать ФС через fuse: 9pfuse Допустим, у нас есть скрипт hello, который доступен по PATH. Если запустить acme и вписать hello в заголовок главного окна (там где Newcol Kill Putall Dump..), а потом нажать на hello 2-й кнопкой мыши, то скрипт запустится. Когда скрипт запускается в рамках acme, то переменная окружения winid содержит номер текущего окна или 0, если нет никаких открытых окон, кроме главного. Для работы с файловой системы acme пока будем пользоваться 9p. ==== #!/bin/sh 9p ls acme 9p read acme acme/$winid/tag ==== Запустите этот вариант скрипта и увидите в отдельном окне корень ФС acme и список пунктов меню вашего текущего активного окна (если такое есть). То же самое можно сделать с fuse: ==== #!/bin/sh mnt=`mktemp -d /tmp/acmeXXXX` 9pfuse `namespace`/acme $mnt ls $mnt cat $mnt/$winid/tag fusermount -u "$mnt" && rmdir "$mnt" ==== На самом деле, в теории, fuse вариант удобнее и быстрее, но я столкнулся с проблемой. Мой ноутбук с ArchLinux не хотел уходить в сон, пока есть хоть одна подмонтированная точка fuse. Так что приходится всё время монтировать и размонтировать, что не очень удобно. В man 4 acme (из plan9port) описана файловая система acme. Я не буду здесь пересказывать эту информацию, но отмечу только некоторые моменты с которыми столкнулся. 1. По идее, мы можем считать позицию селектора (курсор + выделение) с помощью записи в ctl строки addr=dot и и последующего чтения из addr. Однако, при открытии addr он каждый раз ресетится. Таким образом, с помощью 9p вы не сможете получить текущую позицию курсора, так как это две операции: запись addr=dot и чтение addr. А нужно, открыть (и не закрывать addr), потом записать addr=dot, потом прочитать addr. Это можно проделать при использовании 9pfuse. Например: ==== pos=`{ echo 'addr=dot' >> $mnt/$winid/ctl; cat; }<$mnt/$winid/addr` ==== 2. Если добавлять в окно текст (в data), содержащий в себе переводы строк, то просто так читать из event построчно не получится. Потому что первая строка будет содержать событие, а следующая -- уже просто кусок текста и так далее. Да, число символов текстового блока тоже при этом приходит, но писать обработку такого протокола на shell неудобно. Поэтому я добавлял текст только построчно. # Версия на shell Первую черновую версию я написал на shell. Всё, что она делала -- показывала последние 100 писем и реагировала на нажатие средней кнопки мыши на путь к письму (при этом, нужно было сначала этот путь выделить). Она была неудобна, неполна. Но очень проста. Поэтому, в качестве иллюстраций я привожу этот вариант целиком: ==== #!/bin/sh mail_view() { mu view "$MAILDIR/$2" --nocolor | 9p write acme/$1/data echo -n 'clean' | 9p write acme/$1/ctl toline $1 0 } mail_ls() { mu find --nocolor -s d --reverse -f "l|d|f|s" "" | \ /usr/bin/sed -e 's|'$MAILDIR'/\([^ ]\+\)|\1|g' | \ head -n 100 | 9p write acme/$1/data echo -n 'clean' | 9p write acme/$1/ctl toline $1 0 } toline() { echo -n "$2" | 9p write acme/$1/addr echo -n 'dot=addr' | 9p write acme/$1/ctl echo -n 'show' | 9p write acme/$1/ctl } if [ -z "$winid" ]; then exit 1 fi # создаём новое окно winid=`9p read acme/new/ctl | awk '{ print $1 }'` # показываем 100 сообщений mail_ls $winid # добавляем "кнопку" Get echo -n "Get" | 9p write acme/$winid/tag # цикл обработки событий 9p read acme/$winid/event | while read a b c d e; do if echo "$a" | grep -q -e '^Mx' 2>/dev/null; then # mx if [ "$e" = "Get" ]; then mail_ls $winid continue fi elif echo "$a" | grep -q -e '^ML' 2>/dev/null; then if [ -f "$MAILDIR/$e" ]; then msgid=`9p read acme/new/ctl | awk '{ print $1 }'` mail_view $msgid $e continue fi fi echo "$a $b" | 9p write acme/$winid/event 2>/dev/null done ==== Кстати, этот текст был вставлен в статью так: - ввел в tagline

Re: Редактор acme: пишем почтовый клиент

[std.hugeping] Wed Oct 13 17:30:21 UTC 2021 @vvs -> hugeping

hugeping> Кстати, об одёжке. Уверен, у 99% людей пропадает интерес к acme как только они узнают, что в нём нет подстветки синтаксиса. Или, что в этом редакторе нельзя перемещаться по строкам вверх-вниз с помощью клавиатуры... Я зря это сейчас сказал, да? :) Так ведь почему кому-то нравится Emacs? Причины могут быть разные, но я назову одну: Emacs - это интерфейс к elisp. Есть стремление всё делать средствами одного языка. А ACME - это ведь не язык, а чистый интерфейс и предполагает использование дополнительных средств. С точки зрения языка высокого уровня выгоднее вообще не использовать лишние концепции, такие как ОС. Есть такая тенденция языковой среды, когда начинается с языка и постепенно переходит к решению его средствами любых задач, включая построение интерфейсов. В Web - это, наверное, JavaScript из которого выросла целая платформа Node.js. То же самое наблюдается в Python, OCaml, Haskell и т.д. Когда-то в Xerox из этого придумали целую парадигму ОО программирования в стиле Smalltalk (сильно отличающаяся от Simula 67/C++), где вся среда исполнения и интерфейс - это Smalltalk и пользователь меняет эту среду прямо из языка в реальном времени, избегая цикла компиляция-линкование-запуск. До него что-то похожее делали на Lisp, откуда и родился в итоге Emacs. Концепции же Unix, в целом, довольно низкоуровневые. Кстати, идея ACME пришла из языка Oberon Н.Вирта.

Re: Редактор acme: пишем почтовый клиент

[std.hugeping] Thu Oct 14 04:29:39 UTC 2021 @Andrew Lobanov -> vvs

vvs> Так ведь почему кому-то нравится Emacs? Причины могут быть разные, но я назову одну: Emacs - это интерфейс к elisp. Есть стремление всё делать средствами одного языка. А ACME - это ведь не язык, а чистый интерфейс и предполагает использование дополнительных средств. С точки зрения языка высокого уровня выгоднее вообще не использовать лишние концепции, такие как ОС. Как емаксер со стажем, люблю емакс по куче причин. Во-первых, лисп. Я обожаю лисп и ничего не могу с этим поделать. Во-вторых: это классный текстовый интерфейс а-ля Genera только текстовый. А ещё его можно хачить хоть до посинения. Это отдельный кайф. vvs> Есть такая тенденция языковой среды, когда начинается с языка и постепенно переходит к решению его средствами любых задач, включая построение интерфейсов. В Web - это, наверное, JavaScript из которого выросла целая платформа Node.js. То же самое наблюдается в Python, OCaml, Haskell и т.д. Дайте программисту Тьюринг-полный язык и он рано или поздно напишет на нём всё. vvs> Когда-то в Xerox из этого придумали целую парадигму ОО программирования в стиле Smalltalk (сильно отличающаяся от Simula 67/C++), где вся среда исполнения и интерфейс - это Smalltalk и пользователь меняет эту среду прямо из языка в реальном времени, избегая цикла компиляция-линкование-запуск. До него что-то похожее делали на Lisp, откуда и родился в итоге Emacs. Smalltalk это была действительно крутая концепция. Правда я до сих пор не представляю как можно пользоваться огромной объектной штукой, когда всё мутабельно. Но я особо и не вникал. Так, по верхам pharo тыкал. Кстати, у меня он на старте чистого образа ругается, начиная с 8 версии. Досадно. vvs> Концепции же Unix, в целом, довольно низкоуровневые. Кстати, идея ACME пришла из языка Oberon Н.Вирта. А где можно почитать поподробнее? Я про оберон читал, но что-то поверхностное. И про язык и про систему. Интересуют, конечно, обе штуки. +++ Caesium/0.4 RC1

Re: Редактор acme: пишем почтовый клиент

[std.hugeping] Thu Oct 14 04:45:53 UTC 2021 @Andrew Lobanov -> hugeping

Большое спасибо за отличную статью. Даже если я не начну использовать acme для почты, полезного и интересного почерпнул из неё не мало. Вообще люблю твои статьи. Их и читать приятно и интересно, и зачастую полезно. +++ Caesium/0.4 RC1

Re: Редактор acme: пишем почтовый клиент

[std.hugeping] Thu Oct 14 12:25:21 UTC 2021 @vvs -> hugeping

Если кого-нибудь интересует практическая информация по языкам программирования, то проще начинать с википедии. Конкретно по Оберону там упоминается пару порталов на русском: https://oberon.org/ru https://oberoncore.ru/library/start Я сам запускал Оберон лет двадцать назад. Меня тогда поразил его визуальный интерфейс. Когда гораздо позже я увидел ACME, то сразу вспомнил об Обероне. Поискав информацию убедился, что Пайк действительно позаимствовал идею оттуда. Помню, что тогда там не было никакой системы управления версиями и я качал исходники Оберона поштучно в браузере. Они даже должны у меня где-то до сих пор валяться. Но других интересных языков было много, поэтому именно Оберону я уделил мало внимания и до чтения литературы даже не добрался. Lisp и Smalltalk - это отдельная песня. По ним я даже прочитал учебную литературу. Начинал с GNU Smalltalk, потом сразу перешёл на Squeek. Его пытался использовать на практике, в качестве рабочей системы. Пробовал разобраться с его виртуальной машиной, но меня отпугнула жутко запутанная система её построения. Еще у Smalltalk очень специфичная система управления версиями, в которой ведётся разработка. Поддержка Git там появилась только недавно и тоже довольно непривычная. Насколько я понял, Squeek больше ориентирован именно на конечного пользователя, чем Pharo. В своё время я постепенно переходил с Windows 98 на RedHat Linux несколько лет, когда использовал их параллельно и это был непростой опыт. На Smalltalk же перейти так и не удалось - не хватило мотивации и дальше отдельных экспериментов дело не пошло. Сейчас же я сижу на NixOS и там пока нет поддержки Squeek 5.x. Тогда меня интересовали чисто инженерные аспекты компьютерных наук. Сейчас же меня интересует, в основном, логика и теория формальных языков, да и то не как самоцель, а как средство более глубокого понимания других наук, которые можно на них формулировать. Так что мне сейчас интересней устройство компиляторов, а не их практическое использование. Впрочем, исключение составляют языки с зависимыми типами, такие как Coq, Lean или Agda. На них можно формализовывать различные математические теории и доказывать теоремы, что мне и нужно. Так что в данный момент штудирую теоретическую литературу, коей хватит на несколько лет, а временами решаю задачки по логике, что лучше любой игры. На эксперименты с другими языками времени уже не остаётся - это всё больше в прошлом. P.S. Кстати, Lean использует Emacs или VSCode в качестве интерфейса пользователя. В отличие от многих других языков, здесь это не просто удобство, а совершенно необходимая часть взаимодействия с языком.

copyleft 2021 difrex at lessmore dot pw; source code