elixir-editors / emacs-elixir

Emacs major mode for Elixir
448 stars 94 forks source link

License GPL 3 Build Status NonGNU ELPA MELPA Stable MELPA

WARNING There is a built-in Elixir mode with tree-sitter support from Emacs 30+ that can also be used with Emacs 29 This repository is for an older elixir-mode built with SMIE (Simple Minded Indentation Engine) which is not as advanced as tree-sitter for a language like Elixir.

Elixir Mode

Provides font-locking, indentation and navigation support for the Elixir programming language.

Installation

elixir-mode is available on NON-GNU ELPA, MELPA STABLE and MELPA.

Via package.el

package.el is the built-in package manager in Emacs.

You can install elixir-mode with the following command:

M-x package-install [RET] elixir-mode [RET]

or by adding this bit of Emacs Lisp code to your Emacs initialization file (.emacs or init.el):

(unless (package-installed-p 'elixir-mode)
  (package-install 'elixir-mode))

If the installation doesn't work try refreshing the package list:

M-x package-refresh-contents [RET]

Keep in mind that MELPA packages are built automatically from the master branch, meaning bugs might creep in there from time to time. Never-the-less, installing from MELPA is the recommended way of obtaining Elixir-Mode.

MELPA Stable contains packages released from our tags.

Via use-package

Since Emacs 29, use-package is a built-in feature. For versions prior to 29 one can also install it from MELPA (and MELPA Stable).

To install elixir-mode using use-package one can:

(use-package elixir-mode
  :ensure t)

Usage

Interactive Commands

Command (For the M-x prompt.) Description
elixir-mode Switches to elixir-mode.
elixir-mode-open-github Open the GitHub page for Elixir.
elixir-mode-open-elixir-home Go to Elixir README in the browser.
elixir-mode-open-docs-master Open the Elixir documentation for the master.
elixir-mode-open-docs-stable Open the Elixir documentation for the latest stable release.
elixir-mode-show-version Print version info for elixir-mode.

Configuration

Any file that matches the glob *.ex[s] or *.elixir is automatically opened in elixir-mode, but you can change this functionality easily.

;; Highlights *.elixir2 as well
(add-to-list 'auto-mode-alist '("\\.elixir2\\'" . elixir-mode))

Keymapping

Keymaps can be added to the elixir-mode-map variable.

Pairing

Smartparens has direct support for Elixir.

Alternatively, if you want to use ruby-end-mode, you can add the following to your elixir-mode-hook:

(add-to-list 'elixir-mode-hook
             (defun auto-activate-ruby-end-mode-for-elixir-mode ()
               (set (make-variable-buffer-local 'ruby-end-expand-keywords-before-re)
                    "\\(?:^\\|\\s-+\\)\\(?:do\\)")
               (set (make-variable-buffer-local 'ruby-end-check-statement-modifiers) nil)
               (ruby-end-mode +1)))

Notes

This package is tested only with a single version of OTP and 3 versions of Elixir. Please, always report versions (Emacs, Elixir and Erlang/OTP) when raising issues.

Elixir Tooling Integration

You can use web-mode.el to edit elixir templates (eex files).

mix.el provides a minor mode for integration with Mix, a build tool that ships with Elixir.

exunit.el provides ExUnit integration.

Elixir Format

This mode can call mix for formatting code. When inside an elixir buffer, just type M-x elixir-format.

To automate that, you can add this command to the before-save hook.

;; Create a buffer-local hook to run elixir-format on save, only when we enable elixir-mode.
(add-hook 'elixir-mode-hook
          (lambda () (add-hook 'before-save-hook 'elixir-format nil t)))

To use a .formatter.exs you can either set elixir-format-arguments globally to a path like this:

(setq elixir-format-arguments (list "--dot-formatter" "/path/to/.formatter.exs"))

or you set elixir-format-arguments in a hook like this:

(add-hook 'elixir-format-hook (lambda ()
                                 (if (projectile-project-p)
                                      (setq elixir-format-arguments
                                            (list "--dot-formatter"
                                                  (concat (locate-dominating-file buffer-file-name ".formatter.exs") ".formatter.exs")))
                                   (setq elixir-format-arguments nil))))

In this example we use Projectile to determine if we are in a project and then set elixir-format-arguments accordingly.

Please note that this code snippet may cause unhappiness if there is no .formatter.exs file available.

Tips & Tricks

Prettify symbols

Emacs supports font ligatures. For enabling it for Elixir you can add it to your configuration:

(add-hook
 'elixir-mode-hook
 (lambda ()
   (push '(">=" . ?\u2265) prettify-symbols-alist)
   (push '("<=" . ?\u2264) prettify-symbols-alist)
   (push '("!=" . ?\u2260) prettify-symbols-alist)
   (push '("==" . ?\u2A75) prettify-symbols-alist)
   (push '("=~" . ?\u2245) prettify-symbols-alist)
   (push '("<-" . ?\u2190) prettify-symbols-alist)
   (push '("->" . ?\u2192) prettify-symbols-alist)
   (push '("<-" . ?\u2190) prettify-symbols-alist)
   (push '("|>" . ?\u25B7) prettify-symbols-alist)))

;; Or if you use use-packge

(use-package elixir-mode
 :hook (elixir-mode . (lambda ()
    (push '(">=" . ?\u2265) prettify-symbols-alist)
    (push '("<=" . ?\u2264) prettify-symbols-alist)
    (push '("!=" . ?\u2260) prettify-symbols-alist)
    (push '("==" . ?\u2A75) prettify-symbols-alist)
    (push '("=~" . ?\u2245) prettify-symbols-alist)
    (push '("<-" . ?\u2190) prettify-symbols-alist)
    (push '("->" . ?\u2192) prettify-symbols-alist)
    (push '("<-" . ?\u2190) prettify-symbols-alist)
    (push '("|>" . ?\u25B7) prettify-symbols-alist))))

Formatting

If you have issues with the formatter provided by this package, you can try using the format function of language servers. Elixir-ls supports this.

One way to configure this with eglot and use-package would be:

(use-package elixir-mode
 :hook (elixir-mode . eglot-ensure)
 (before-save . eglot-format))

Syntax highlighting for LiveView (and similar techniques for other syntaxes)

When you need different major modes in the SAME buffer, Emacs do not provide a standard way. There is an external package that can help here by adding more than one major mode called poly-mode.

A possible configuration would be:

;; Assumes web-mode and elixir-mode are already set up
;;
(use-package polymode
  :mode ("\.ex$" . poly-elixir-web-mode)
  :config
  (define-hostmode poly-elixir-hostmode :mode 'elixir-mode)
  (define-innermode poly-liveview-expr-elixir-innermode
    :mode 'web-mode
    :head-matcher (rx line-start (* space) "~H" (= 3 (char "\"'")) line-end)
    :tail-matcher (rx line-start (* space) (= 3 (char "\"'")) line-end)
    :head-mode 'host
    :tail-mode 'host
    :allow-nested nil
    :keep-in-mode 'host
    :fallback-mode 'host)
  (define-polymode poly-elixir-web-mode
    :hostmode 'poly-elixir-hostmode
    :innermodes '(poly-liveview-expr-elixir-innermode))
  )
(setq web-mode-engines-alist '(("elixir" . "\\.ex\\'")))

History

This mode is based on the Emacs mode by secondplanet.

Contributing

Please read CONTRIBUTING.md for guidelines on how to contribute to this project.

License

Copyright © 2011-2017 Samuel Tonini, Matt DeBoard, Andreas Fuchs, secondplanet and contributors.

Distributed under the GNU General Public License, version 3