Distell -- друг Erlang разработчика

30 ноября 2012

Жил я долго и счастливо без Distell, используя Emacs и erlang-mode, и чувствовал себя неплохо. Помимо всего прочего, использовал я sync. Sync представляет собой otp-приложение, которое нужно запустить на ноде, после чего он собирает инфу обо всех сорцах и следит за изменениями в них. Как только какие-то сорцы меняются, sync перекомпилирует их и перезагружает измененные модули на ноде. Довольно удобно -- редактируешь код, сохраняешь файл, и изменения тут же подхватываются.

Однако через какое-то время sync стал утомлять своими побочными эффектами. К сожалению, нельзя указать, за какими именно сорцами нужно следить. Sync сам берет инфу из рантайма и следит не только за моим кодом, но и за всеми зависимостями в папочке deps. Вроде бы это не должно мешать, ведь исходники зависимостей не меняются. Но нет, почему-то sync перегружает модули epgsql (драйвер для работы с PostgreSQL) и yaws (веб-сервер). Случается это много раз за день, в непредсказуемые моменты времени, и сопровождается обильными сообщениями на много экранов в консоли. Этот лишний информационный шум изрядно затруднял чтение интересующих меня сообщений. В итоге это мне надоело, и я стал присматриваться к альтернативам.

Это была одна причина попробовать Distell. Есть и другая.

Примерно месяц я работал в Intellij IDEA с Erlang-плагином. По причинам, о которых будет сказано позже, в другом посте, я решил вернуться к Emacs. Но теперь уже возможностей erlang-mode мне было мало. А Distell, как оказалось, дает практически все, что есть в Intellij IDEA.

Я готовлю подробное сравнение Intellij IDEA + Erlang-плагин с одной стороны, и Emacs + ido + erlang-mode + Distell с другой стороны. Об этом будет отдельная статья. Пока скажу только, что IDEA будет очень хорошим вариантом для тех, кто хотел бы разрабатывать на Erlang, но привык пользоваться мощной IDE и неуютно чувствует себя в Emacs.

Итак, в чем фишка Distell? Он представляет собой расширение Emacs, которое умеет общаться с работающей Erlang-нодой. Таким образом он имеет всю актуальную информацию из рантайма и может выполнять свои запросы на ноде. Поэтому ему не нужен синтаксический анализ исходного кода, Distell и так знает, какие модули и функции там есть. Отсюда умный автокомплит, навигация по коду, заменяющая ненужный TAGS, find usages и т.д.

Distell умеет показать список процессов и инфу по ним, умеет интегрироваться с профайлером и дебагером, показывать документацию и т.д. Это все описано у Алекса Отта и еще тут, так что я повторяться не буду. А лучше я расскажу о том, о чем не написано.

Итак, нам нужна замена sync, чтобы с удобством обновлять измененные модули на ноде. Distel предлагает две функции:

Первая перегружает один модуль, соответствующий активному буферу. Вторая перегружает все измененные модули. Но прежде, чем перегрузить модуль, его нужно скомпилировать. Этого Distel сам не делает.

В erlang-mode есть функция erlang-compile, но она практически бесполезна. Потому как наш проект, конечно, является otp-приложением правильной структуры (ebin, include, src, priv, test), и собирать его нужно через rebar :)

Можно воспользоваться более общей функцией compile, если сказать ей, какой командой собирать проект. Например:

  (compile "cd ..; rebar compile")

или

(compile "cd ..; make")

если у вас есть свой Makefile с более сложной сборкой.

Итак, сперва собираем

  M-x compile "cd ..; make"

Потом перегружаем

  C-c C-d L

Гм, неудобно. Много букв писать надо :) Ну для начала зададим дефолтную команду компиляции:

(setq-default compile-command "cd ..; make")

Затем повесим шоткат на compile, и шоткат попроще на reload:

  (global-set-key [f6] 'compile)
  (global-set-key [f7] 'erl-reload-module)
  (global-set-key [f8] 'erl-reload-modules)

Уже лучше. F6, F7 и готово. Но все-таки хотелось бы сделать все в одно действие. Не вопрос, пишем свою функцию и вешаем шоткат на нее:

(defun my-sync()
  (interactive)
  (compile "cd ..; make")
  (erl-reload-modules))
(global-set-key [f8] 'my-sync)

И вот тут начинаются проблемы. Оказывается erl-reload-modules без аргументов вызвать нельзя, она требует ссылку на ноду. Ссылку эту взять негде. Пришлось лезть в сорцы Distell и глядеть, как там все реализовано. Оказалось, там есть функция erl-target-node. Но мой скрипт ее не видит и не может вызывать. Посему нужно залезть в path/to/distell/elisp/erl-service.el, найти 26 строку, где определена erl-reload-modules, и добавить (interactive). После этого ее можно вызывать :)

Учтем еще пару нюансов:

Посему нужно делать так:

(defun my-sync()
  (interactive)
  (save-buffer)
  (compile "cd ..; make") ;; asyn call, emacs doesn't wait for compilation end
  (sleep-for 2)
  (erl-reload-modules (erl-target-node)))
(global-set-key [f8] 'my-sync)

Теперь все работает как надо.

comments powered by Disqus