Я хочу, чтобы Emacs отправлял фрагменты текста из любого буфера в Slack. К счастью, для Emacs есть Slack-клиент под названием emacs-slack. Итак, большая часть тяжелой работы сделана за меня — у меня есть интерфейс для Slack в Emacs, который позволяет мне отправлять и читать сообщения в любом канале, комнате или прямом сообщении. К сожалению, сейчас я нахожусь в зловещей долине. Я думаю, что emacs-slack делает много вещей правильно, но я не думаю, что он делает совместное использование так просто, как должно быть.
Почему?
Почему я использую Emacs? В основном потому, что мне нравится что-то ломать, а мне очень легко сломать настройку Emacs.
Нет, почему…
Почему я использую Slack внутри Emacs? Мне нравится мой Slack, как и мой Emacs: хрупкий и склеенный тайной магией, которую я не понимаю. Также есть дополнительное преимущество: Slack в Emacs требует меньше ресурсов, чем настольное приложение Slack.
Нет! Почему emacs-slack недостаточно хорош?
О, ну, потому что я разработчик, и мне нравится делиться вещами, и я ленив — слишком ленив, чтобы копировать и вставлять каждый раз, когда я хочу поделиться идеей или задать вопрос по строке кода. Поэтому вместо того, чтобы копировать и вставлять, я планирую потратить слишком много времени на чтение чужого кода, выясняя, как упростить обмен кодом с моей командой.
Ты, эм, не хочешь прокатиться, не так ли?
TLDR
Установите emacs-slack и вставьте приведенный ниже код в свой файл инициализации, чтобы иметь возможность отправить фрагмент кода в Slack.
Давайте играть!
Если вы хотите продолжить, вам нужно будет установить и настроить emacs-slack.
Установить
Если вы используете use-package в качестве менеджера пакетов Emacs, у Yuya373 есть некоторый код в репозитории emacs-slack, который вы можете просто скопировать и вставить в свой файл инициализации.
Если вы не используете use-package, вы можете использовать borg, чтобы ассимилировать пакет в ваш Emacs.
Если вы не используете borg, вы можете использовать el-get для установки пакетов за вас.
Если у вас нет ни одного из них, хороший простой M-x package-install emacs-slack творит чудеса.
Если вы не хотите делать ничего из этого, вы можете отказаться от хорошей жизни и использовать vim.
Настраивать
Чтобы настроить emacs-slack, просто следуйте инструкциям на странице GitHub.
Играть в
Если вы никогда раньше не слышали о emacs-slack, этого вам, вероятно, будет достаточно. У вас настроен и работает emacs-slack. Играть с этим. Проживи это. Любить это. Забудьте об этом посте.
Все еще здесь? Давайте погрузимся
Хватит о тебе! Вечер субботы, у меня нет свидания, двухлитровая бутылка Шасты и мой микстейп all-Rush — давай зажигаем.
Теперь все, что мне нужно сделать, это выяснить, какие функции в emacs-slack отвечают за отправку сообщений, как подключиться к этим функциям и как я могу получить программный доступ к тексту в Emacs. Простой? Простой.
слабина.эл
Моими первыми шагами будут открытие некоторых файлов emacs-slack и проверка того, что я могу угадать.
slack.el кажется хорошим местом для начала. Давайте посмотрим, есть пара defcustoms, несколько defun и cl-defuns¹… но ничего похожего на то, что это связано с отправкой сообщений, что, в конечном счете, я и хочу сделать.
Время двигаться дальше…
slack-message.el
Я хочу отправить сообщение, может лучше начать с slack-message.el.
Хорошо… подождите, что? дефкласс? дефметод? С каких это пор Emacs Lisp стал объектно-ориентированным²?
Итак, похоже, что Message — это объект с множеством методов. Полезно, но пока ничего не дает.
Я сдаюсь
Я прочитал два файла, я узнал кое-что о структуре этого пакета, но там 50 файлов .el. Если я продолжу просматривать все эти файлы, работы будет много! Может быть, я могу использовать другой подход.
Благодаря силе профилировщика меня не победить!
Я знаю два способа узнать, как работает код в Emacs, помимо простого чтения исходного кода. Первый — профилировщик, встроенный прямо в Emacs, а второй — отладчик Elisp, Edebug. Сейчас я начну с профилировщика — более простого подхода.
M-x profiler-start будет отслеживать ЦП, память или их комбинацию. Моя первая мысль — отправить несколько сообщений в Slack и посмотреть, что появится в профилировщике. Надеюсь, это укажет мне правильное направление, и я найду магические заклинания, которые мне нужно произнести, чтобы моя идея заработала.
Заглянуть за завесу
Давайте посмотрим на верхний уровень трассировки профиля, найденный в буфере ЦП. Только два вызова выглядят заслуживающими изучения: `command-execute` и `…`.
Процессор
+ command-execute 67% + redisplay_internal (C function) 21% + lui-scroll-post-command 9% + #<compiled 0x4da9630d> 0% + emojify-update-visible-emojis-background-after-command 0% + company-post-command 0% + request--curl-callback 0% + ... 0% + timer-event-handler 0% + undo-auto--add-boundary 0% + sp--save-pre-command-state 0% + global-hl-line-highlight 0%
Команда-выполнение
Развернув command-execute, мы начинаем видеть некоторые интересные вызовы…
Усилить
- command-execute 67% - call-interactively 67% - apply 67% - call-interactively@ido-cr+-record-current-command 63% - apply 63% - #<subr call-interactively> 63% - funcall-interactively 63% + profiler-report 63% - lui-send-input 0% - slack-message--send 0% - let* 0% - if 0% - let* 0% - if 0% - slack-buffer-send-message 0% - apply 0% - #<compiled 0x4f23dd71> 0% - apply 0% - #<compiled 0x4db1669d> 0% - apply 0% - #<lambda 0xdafed4764d8> 0% - let* 0% - slack-message-send-internal 0% - let* 0% - let* 0% + slack-ws-send 0% + json-encode 0% + list 0% + slack-message-create 0%
slack-message — send и slack-message-send-internal кажутся наиболее многообещающими, поэтому давайте рассмотрим их.
Это похоже на slack-message — send проверяет, является ли текущий буфер «Slack Buffer», ищет «Slack Commands» для выполнения в буфере, а затем передает сообщение другой функции slack-buffer-send-message. К сожалению, похоже, что это слишком сильно зависит от внутреннего состояния пакета, поэтому давайте перейдем к следующей функции и надеемся, что она будет проще.
Следующим в моем списке является slack-message-send-internal. Это сразу выглядит многообещающе. Он принимает именно те данные, которые я ожидал: сообщение, идентификатор комнаты и команду. Затем он объединяет данные в список с ключами и отправляет закодированный объект JSON через WebSocket. Джекпот!
Теперь о моей следующей проблеме…
Прослушивание
Профилировщик Emacs хорошо видит, что вызывается, но как мне увидеть, как выглядят структуры данных? Я имею в виду, что мне нужно знать, как они выглядят, чтобы вставить их во внутреннюю отправку сообщений, верно?
Эдебаг спешит на помощь! Если у вас есть намерение написать elisp, я рекомендую вам прочитать этот раздел Руководства по Emacs. Я только недавно открыл для себя Edebug, но он быстро стал бесценным инструментом при изучении кода.
Трассировка через slack-message-send
Я знаю, какую функцию я хочу проверить, slack-message-send-internal, но мне также любопытно: как данные трансформируются и формируются по мере их прохождения через эту систему? Чтобы ответить на этот вопрос, нам нужно начать проверку ранее в цепочке вызовов. Мы уже бегло рассмотрели slack-send–message, поэтому давайте добавим точку останова источника и инструментируем функцию.
Я добавил точку останова в функцию edebug. Теперь нам просто нужно инструментировать функцию. Простой способ инструментирования функций — переместить курсор в начало определения функции и вызвать M-x edebug-eval-top-level-form. Это оценивает текущую функцию и инструментирует ее, чтобы Edebug мог творить свое волшебство.
После отслеживания функций я вижу, что сообщение, идентификатор канала и команда имеют следующую структуру:
Похоже, сообщение может быть любой строкой. Мне все еще нужно выяснить, как выбрать команду и канал, в который я хочу публиковать сообщения.
слабый выбор канала
К счастью, я знаю, где искать. Каждый раз, когда я хочу войти в Slack-канал, я запускаю команду M-x slack-channel-select, так что давайте посмотрим на это.
Это выглядит идеально. Я могу скопировать и вставить 90% этого кода в свою собственную функцию, и у нас будет что-то близкое к работе.
Итак, Фу заходит в бар
Мой первый тест состоял в том, чтобы посмотреть, смогу ли я быстро изменить эту функцию, чтобы получить работающий прототип.
Теперь, чтобы проверить это!
Это прекрасно работает! Теперь откупорить бутылку Шасты, которую я копил.
Буферы, регионы и все, что между ними
Теперь последняя проблема, которую мне нужно решить: мне нужно выяснить, как копировать область текста. Я не уверен, как это сделать, но я знаю отличный ресурс для изучения elisp, собственное Elisp Manual Emacs ⁴. Ключевые части, о которых нам нужно знать в этом руководстве, — это Регионы и Содержимое буфера.
В качестве примера того, как я научился программно получать доступ к тексту в области, я описал ниже простую функцию, которая выводит содержимое выбранной области в минибуфер.
Это, наконец, привело меня к тому, что у меня есть все инструменты для создания функции, в которой я могу публиковать сообщения из любого буфера в slack.
Кодификация моего сообщения
Последнее улучшение моей функции, которое я хочу сделать: я почти всегда буду отправлять какой-то фрагмент кода в Slack, поэтому я хочу заключить его в три обратных кавычки, чтобы Slack применил к нему правильную разметку.
1700 слов для описания 10-строчной функции, я не понимаю всей ненависти, которую получает Emacs.
Обновлять
Прочтите мое продолжение Взлом Emacs для отправки текста в Slack: ускорение, где я исследую, как очистить код и сделать его более универсальным.
[1] Когда я читал слабый код, мне показалось интересным, что defun в Emacs отличается от реализации defun в CL. Ричарду Столмену не нравилось, как в Common Lisp можно было использовать ключи для деструктуризации аргументов, и он решил опустить эту возможность в elisp. https://www.emacswiki.org/emacs/KeywordArguments
[2] Забавное замечание: Emacs Lisp имеет объектную систему Расширенная реализация интерпретируемых объектов Emacs по крайней мере с 2007 года, а может быть и раньше ³.
[3] На самом деле EIEIO вдохновлен Объектной системой Common Lisp, и это погружение в Emacs-Slack дало мне много нового об Emacs и Common Lisp!
[4] Это ложь, я не знал об этом мануале, пока не начал писать этот пост. Это сэкономило бы мне часы очень плохого гугл-фу.