Modern Emacs setup for Erlang (with autocompletion and lint)

I have recently started programming Erlang again, and I also decided to retrain myself as a terminal Emacs user (I'm nostalgic for the '80s and my old glorious green and black terminal :P).

Switching to an editor for me requires having auto-completion and auto-lint working in the languages I use, or I get very cross fast.

I haven't found any complete and recent guide for setting up Emacs for Erlang, so I'm calling this guide "Modern".

You'll probably want to setup package management with Cask and pallet for your Emacs, follow this guide for that.

I assume you have a clean ~/.emacs file and that you'll put any git repos we'll clone in ~/.emacs.d.

The first choice to make is: EDTS (more like an IDE) or a self-made setup?

EDTS

I tried EDTS, but I get the huge slowdown he mentions in the README.md, so that's a no-go for me. Also I don't really like to have all the choices already made, I prefer to figure out myself what functionality I want.

I posted a guide on how to install EDTS, because I found it a bit treacherous.

Self-made

So, to get syntax highlighting, lint and autocomplete, the packages we need are:

  • erlang-mode, it should be in your Erlang install (how to install Erlang with kerl if you don't have one)
  • distel
  • popup
  • company
  • flycheck
  • flycheck-tip
  • company-distel

Install deps

The first thing you'll want to do is hit M-x package-list-packages and install:

  • popup
  • company
  • flycheck
  • flycheck-tip

or you can put them in your Cask file and do a cask install from the cli.

erlang-mode

Find where your Erlang mode is. If you installed Erlang following my guide it will be wherever you installed the kerl build, and then: <erl-dir>/lib/tools-<version>/emacs.
I usually install kerl builds following the pattern ~/.erlangs/<version>.

Once you have that path, add this to your .emacs or init.el, or equivalent file:

(setq load-path (cons "<erl-dir>/lib/tools-<version>/emacs" load-path))
(require 'erlang-start)
(setq erlang-root-dir "<erl-dir>/")
(setq exec-path (cons "<erl-dir>/bin" exec-path))
(setq erlang-man-root-dir "<erl-dir>/man")

Check that it worked by opening an Erlang file, you should see "Erlang" in the mode, and it should syntax highlight properly.

Flycheck

You should have Flycheck installed from before, so add these lines to your .emacs or equivalent:

(require 'flycheck)

(flycheck-define-checker erlang-otp
                         "An Erlang syntax checker using the Erlang interpreter."
                         :command ("erlc" "-o" temporary-directory "-Wall"
                                   "-I" "../include" "-I" "../../include"
                                   "-I" "../../../include" source)
                         :error-patterns
                         ((warning line-start (file-name) ":" line ": Warning:" (message) line-end)
                          (error line-start (file-name) ":" line ": " (message) line-end)))

(add-hook 'erlang-mode-hook
          (lambda ()
            (flycheck-select-checker 'erlang-otp)
                        (flycheck-mode)))

If you open an Erlang file now and start typing, errors (highlighted in red) and warnings (highlighted in yellow) should show up and you should have a "FlyC" in the modes.

Flycheck has a lot of functionality, to learn more this is a good article.

Tip: C-c ! l will give you a list of all your errors and warnings.

Flycheck tip

If you'd like to get the messages from flycheck to show up as tooltips, use flycheck-tip.

You installed it as a package, so it should be sufficient to add this to your .emacs or equivalent:

(require 'flycheck-tip)
(flycheck-tip-use-timer 'verbose)

You can find more options at its github page.

If you open an Erlang file with some errors and warnings now, they should show as a tooltip when your cursor is on them.

Distel

Installing Distel requires cloning it from its github repo to ~/.emacs.d/distel or anywhere you prefer.

You'll to go to the director, hit make, it will compile the Erlang code. You might need to add it to your Erlang code paths, depending on your Erlang install.
If you want the docs, cd doc && make info && sudo make install.
info distel to read them.

Then add the path to your .emacs file, require and setup:

(push "~/.emacs.d/distel/elisp/" load-path)
(require 'distel)
(distel-setup)

To make your life easier you'll probably want to give it a default node name:

;; prevent annoying hang-on-compile
(defvar inferior-erlang-prompt-timeout t)
;; default node name to emacs@localhost
(setq inferior-erlang-machine-options '("-sname" "emacs"))
;; tell distel to default to that node
(setq erl-nodename-cache
      (make-symbol
       (concat
        "emacs@"
        ;; Mac OS X uses "name.local" instead of "name", this should work
        ;; pretty much anywhere without having to muck with NetInfo
        ;; ... but I only tested it on Mac OS X.
                (car (split-string (shell-command-to-string "hostname"))))))

(taken from this post)

Distel runs Erlang nodes, and has a lot of functionality, you should look at the docs to learn it.

The main commands you need to get started are:

  • C-c C-z for starting the node
  • M-. when the cursor is on a function to show its definition
  • M-TAB to autocomplete

Unless a node is started, most functionality won't work, so start a node first.

I suggest you try whether that worked now. Autocomplete is a bit cumbersome, because it shows in a different buffer.

We'll fix that now.

Auto complete

Last but not least, getting auto complete to work conveniently is not really documented anywhere that I could find.

We need to setup the company package we installed before:

(add-hook 'after-init-hook 'global-company-mode)

added to your ~/.emacs will activate company for all buffers.

Now it needs a source to autocomplete from, and company-distel is the package for that. You have to clone it from github, as before in ~/.emacs.d/ will do.

Then add this to your .emacs:

(push "~/.emacs.d/company-distel/" load-path)
(require 'company-distel)
(with-eval-after-load 'company
  (add-to-list 'company-backends 'company-distel))
(require 'company-distel-frontend)

At this point your autocomplete from Distel will show up in a convenient popup. If it doesn't, try M-x company-complete, and that should give it a push.

Watch out if you use company-mode for more than one language, you'll likely need to hook it to the mode, instead of just adding it to the backends list:

(add-hook 'erlang-mode-hook
          (lambda ()
            (setq company-backends '(company-distel))))

That's all, hopefully it worked. If not please let me know.