Я хочу, чтобы 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] Это ложь, я не знал об этом мануале, пока не начал писать этот пост. Это сэкономило бы мне часы очень плохого гугл-фу.