Minimal Setup for Elixir development in Emacs
Tags: elixir, emacs
Published: 2019-11-06 06:29:18

---
title: Minimal Setup for Elixir development in Emacs
published: true
date: 2019-10-19 15:00:15 UTC
tags: elixir, emacs
canonical_url: https://erick.navarro.io/blog/minimal-setup-for-elixir-development-in-emacs/
---

I've been working professionally with Elixir for 7 months, obviously using emacs as my daily editor. In this post we'll see some packages that could be useful for Elixir development.

I'm not using LSP (yet), so the goal here is just to have a minimal setup for Elixir development.

We're going to use `use-package` to install all the needed packages.

## Syntax highlighting

We'll use [Elixir-mode](https://github.com/Elixir-editors/emacs-Elixir/). This package give us syntax highlighting support and some useful features like `mix-format` which let us format our code using `mix format` task, which is available since Elixir 1.6

```emacs-lisp
(use-package elixir-mode
    :ensure t
    :bind (:map elixir-mode-map
                ("C-c C-f" . elixir-format)))
```

## Go to definition

A LSP backed package like `elixir-lsp` could be way more accurate for this functionality but now we're going to use [dumb-jump](https://github.com/jacktasia/dumb-jump).

This package give us "go to definition" functionality just using regex. It has support for Elixir out the box and it works pretty well.

We'll use `helm` to show the different options when dumb-jump finds more than one definition for the same term.

```emacs-lisp
(use-package dumb-jump
    :ensure t
    :init
    (setq dumb-jump-selector 'helm))
```

## Snippets

Using snippets can improve the speed of writing code. I'm using [yasnippet](https://github.com/joaotavora/yasnippet), a snippets template system, and also a set of snippets(for many languages including Elixir) called [yasnippet-snippets](https://github.com/AndreaCrotti/yasnippet-snippets).

We can install these packages with the following code. Notice that we're enabling yasnippet only for a few modes. If you want to you can enable it globally using `(yas-global-mode 1)`.

```emacs-lisp
(use-package yasnippet
    :ensure t
    :hook ((prog-mode . yas-minor-mode)
           (conf-mode . yas-minor-mode)
           (text-mode . yas-minor-mode)
           (snippet-mode . yas-minor-mode)))

  (use-package yasnippet-snippets
    :ensure t
    :after (yasnippet))
```

## Running tests

Running tests without leaving our editor is a nice feature to have in any editor/IDE. Unfortunately I haven't found any package that does this, so I wrote some elisp code to accomplish it.

To make this happen we're going to use feature called `compile`. Compile allows us to execute a shell command and print the resulting output in a "compilation" buffer so we can see the results.

We can run tests using `mix test` in different ways

- `mix test`: run all the tests within our project.

- `mix test path_to_test_file.exs`: run the given test file.

- `mix test path_to_test_file.exs:line_number`: run the test defined around the given line number.

There are some other ways but these are the ones we're going to use.

```emacs-lisp
(defun my/mix-run-test (&optional at-point)
    "If AT-POINT is true it will pass the line number to mix test."
    (interactive)
    (let* ((current-file (buffer-file-name))
           (current-line (line-number-at-pos))
           (mix-file (concat (projectile-project-root) "mix.exs"))
           (default-directory (file-name-directory mix-file))
           (mix-env (concat "MIX_ENV=test ")))

      (if at-point
          (compile (format "%s mix test %s:%s" mix-env current-file current-line))
        (compile (format "%s mix test %s" mix-env current-file)))))

  (defun my/mix-run-test-file ()
    "Run mix test over the current file."
    (interactive)
    (my/mix-run-test nil))

  (defun my/mix-run-test-at-point ()
    "Run mix test at point."
    (interactive)
    (my/mix-run-test t))
```

We have three functions, a "private" function called `my/mix-run-test` which will do all the "magic", this function will get some data about the context where it was called. We got the `current-file` and `current-line` from where the function was called, and now we'll use this to build our shell command and then use this shell command to call `compile`.

Now we can create two more functions to expose two different behaviors, running an entire test file or just the test our cursor is placed on. We're going to use the latest to have a keybinding a run the test quickest.

Now we can add this to our previous code:

```emacs-lisp
(use-package elixir-mode
    :ensure t
    :bind (:map elixir-mode-map
                ("C-c C-f" . elixir-format)
                ("C-c C-t" . my/mix-run-test-at-point)))
```

Now if we press `C-c C-t` in a file(`my_test.exs`) where our cursor is on line 10, emacs will build the command `mix test my_test.exs:10` and run it in a compilation buffer.

## Others useful packages

- Projectile: It's a package to handle many projects. It allows us to switch between projects easily.

- Magit: The best interface so far for use git. If you haven't use it you definitively should give it a try.

- direnv-mode: It's a package to load environment variables using a `.envrc` file. It's useful to load all the environment variables you need for a project.

## Conclusion

I use this setup with a few tweaks in my daily work and it works pretty well for my needs.