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 предлагает две функции:
- erl-reload-module C-c C-d L
- erl-reload-modules C-c C-d r
Первая перегружает один модуль, соответствующий активному буферу. Вторая перегружает все измененные модули. Но прежде, чем перегрузить модуль, его нужно скомпилировать. Этого 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). После этого ее можно вызывать :)
Учтем еще пару нюансов:
- перед сборкой нужно сохранить буфер
- compile работает асинхронно, поэтому вызов erl-reload-modules произойдет раньше, чем компиляция закончится.
Посему нужно делать так:
(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