elixir: add flycheck support for mix compile

This commit is contained in:
syl20bnr 2016-05-31 21:34:51 -04:00
parent 8fc53ae311
commit dd572af071
6 changed files with 212 additions and 15 deletions

View File

@ -5,6 +5,8 @@
* Table of Contents :TOC_4_gh:noexport:
- [[#description][Description]]
- [[#install][Install]]
- [[#configuration][Configuration]]
- [[#flycheck-support-for-mix-compile][Flycheck support for mix compile]]
- [[#key-bindings][Key bindings]]
- [[#refcard][Refcard]]
- [[#help][Help]]
@ -18,7 +20,6 @@
- [[#code-definition-jump][Code Definition Jump]]
* Description
This layer adds support for [[http://elixir-lang.org/][Elixir]] .
[[https://github.com/tonini/alchemist.el][Alchemist]] brings the Elixir tooling to Emacs and comes with a bunch of
@ -32,16 +33,38 @@ features like:
- Smart code completion
- Elixir project management
- Integration with [[http://company-mode.github.io/][company-mode]]
- Flycheck support for mix compile
* Install
To use this configuration layer, add it to your =~/.spacemacs=. You will need to
add =elixir= to the existing =dotspacemacs-configuration-layers= list in this
file.
* Configuration
** Flycheck support for mix compile
*Important:*
Elixir compilation is based on macros and is unsafe since arbitrary code can
be run during compilation. Therefore Spacemacs disable compilation flycheck
support by default.
To enable flycheck support for =mix compile= *globally* the variable
=elixir-enable-compilation-checking= must be explicitly set to =t= in your
dotfile.
It is recommended to use directory local variables instead. These variables are
stored in a file named =.dit-local.el= at the root of your project. Their values
are applied only for the project and not outside. To easily add a directory
local variable used ~SPC f v d~ and provide the mode =elixir-mode= then the
variable name =elixir-enable-compilation-checking= and its value.
Spacemacs marks the variable =elixir-enable-compilation-checking= as safe so
Emacs won't ask you if the variable is safe whenever an elixir file is opened.
Remember that you can check the flycheck checkers enabled with ~SPC e v~.
* Key bindings
** Refcard
You find and overview of all the key-bindings on the [[file:alchemist-refcard.pdf][Alchemist-Refcard]].
** Help

View File

@ -11,5 +11,11 @@
;; Variables
(defvar elixir-enable-compilation-checking nil
"If non-nil syntax checking is enable for compilation.
Default is nil because Elixir compilation is based on macros and thus it
is unsafe. Activate this option only for trusted code, usage of a
directory variable is recommended.")
(spacemacs|defvar-company-backends elixir-mode)
(spacemacs|defvar-company-backends alchemist-iex-mode)

View File

@ -14,3 +14,14 @@
(newline-and-indent)
(forward-line -1)
(indent-according-to-mode)))
(defun spacemacs//elixir-enable-compilation-checking ()
"Enable compile checking if `elixir-enable-compilation-checking' is non nil."
(message "djeoifhwfwfjegweifh")
(hack-dir-local-variables)
(let ((enabled (cdr (assq 'elixir-enable-compilation-checking
file-local-variables-alist))))
(message "---> %s" (assq 'elixir-enable-compilation-checking
file-local-variables-alist))
(when (or enabled elixir-enable-compilation-checking)
(elixir-flycheck-mix-compile-setup))))

View File

@ -0,0 +1,62 @@
;;; elixir-flycheck-mix-compile.el --- Elixir flycheck integration -*- lexical-binding: t; -*-
;; Copyright (C) 2016 Tomasz Kowal
;; Author: Tomasz Kowal <tomekowal@gmail.com>
;; Keywords: Elixir flycheck mix
;; Version: 0.0.1
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This package should be distributed with =mix_compile_helper=.
;; Make sure it is in your PATH
;;; Code:
(require 'flycheck)
;; :command uses source-original, source-inplace copies the file
;; which makes mix throw errors
(flycheck-define-checker
elixir-mix-compile
"Defines a checker for elixir with mix compile"
:command ("mix_compile_helper" source-original)
:error-patterns
((warning line-start
(file-name)
":"
line
": warning: "
(message)
line-end)
(error line-start
"** ("
(one-or-more word)
"Error) "
(file-name)
":"
line
": "
(message)
line-end))
:modes (elixir-mode))
;;;###autoload
(defun elixir-flycheck-mix-compile-setup ()
"Setup Flycheck for Elixir."
(add-to-list 'flycheck-checkers 'elixir-mix-compile))
(provide 'elixir-flycheck-mix-compile)
;;; elixir-flycheck-mix-compile.el ends here

View File

@ -0,0 +1,71 @@
#!/usr/bin/env elixir
defmodule :mix_compile_helper do
def run() do
project_root = get_project_root()
if project_root do
{stdout, exit_status} =
System.cmd "mix", ["compile", "--ignore-module-conflict"],
cd: project_root, stderr_to_stdout: true
stdout
|> String.split("\n")
|> Enum.map(&(make_paths_absolute(&1, project_root)))
|> Enum.join("\n")
|> IO.puts
exit({:shutdown, exit_status})
else
usage(:root_not_found)
end
end
def usage(:general) do
file = __ENV__.file |> Path.split |> List.last
IO.puts "Usage:"
IO.puts "Make sure executable is in the path. Run inside mix project"
IO.puts " #{file}"
end
def usage(:root_not_found) do
IO.puts "mix.exs not detected in the directory structure"
usage(:general)
end
defp make_paths_absolute(line, root) do
line
|> make_error_paths_absolute(root)
|> make_warning_paths_absolute(root)
end
defp make_error_paths_absolute(line, root) do
replace_at_position_if_pattern(line, "CompileError", 2, root)
end
defp make_warning_paths_absolute(line, root) do
replace_at_position_if_pattern(line, "warning:", 0, root)
end
defp replace_at_position_if_pattern(line, pattern, position, root) do
if String.contains?(line, pattern) do
line
|> String.split(" ")
|> List.update_at(position, &(Path.absname(&1, root)))
|> Enum.join(" ")
else
line
end
end
def get_project_root do
System.cwd
|> Path.split
|> Enum.reverse
|> get_project_root
end
def get_project_root([]), do: nil
def get_project_root([_|rest] = reversed_path) do
current_path = reversed_path |> Enum.reverse |> Path.join()
mix_file = Path.join(current_path, "mix.exs")
if File.exists?(mix_file), do: current_path, else: get_project_root(rest)
end
end
:mix_compile_helper.run

View File

@ -13,11 +13,19 @@
'(
alchemist
company
(elixir-flycheck-mix-compile
:location local
:toggle (configuration-layer/package-usedp 'flycheck))
elixir-mode
flycheck
popwin
smartparens
))
(defun elixir/post-init-company ()
(spacemacs|add-company-hook elixir-mode)
(spacemacs|add-company-hook alchemist-iex-mode))
(defun elixir/init-alchemist ()
(use-package alchemist
:defer t
@ -109,9 +117,35 @@
(evil-define-key 'normal mode
(kbd "q") 'quit-window))))
(defun elixir/post-init-company ()
(spacemacs|add-company-hook elixir-mode)
(spacemacs|add-company-hook alchemist-iex-mode))
(defun elixir/init-elixir-flycheck-mix-compile ()
(use-package elixir-flycheck-mix-compile
:commands (elixir-flycheck-mix-compile-setup)
:init
(progn
(add-to-list 'safe-local-variable-values
(cons 'elixir-enable-compilation-checking nil))
(add-to-list 'safe-local-variable-values
(cons 'elixir-enable-compilation-checking t))
(add-hook 'elixir-mode-hook
'spacemacs//elixir-enable-compilation-checking t))
:config
;; enable mix_compile_helper executable
(let ((layer-path (configuration-layer/get-layer-path 'elixir)))
(add-to-list 'exec-path
(concat layer-path
"elixir/local/elixir-flycheck-mix-compile")))))
(defun elixir/init-elixir-mode ()
(use-package elixir-mode
:defer t))
(defun elixir/post-init-flycheck ()
(add-hook 'elixir-mode-hook 'flycheck-mode))
(defun elixir/pre-init-popwin ()
(spacemacs|use-package-add-hook popwin
:post-config
(push '("*mix*" :tail t :noselect t) popwin:special-display-config)))
(defun elixir/post-init-smartparens ()
(spacemacs|use-package-add-hook smartparens
@ -129,13 +163,3 @@
:when '(("SPC" "RET"))
:post-handlers '(:add spacemacs//elixir-do-end-close-action)
:actions '(insert))))))
(defun elixir/init-elixir-mode ()
(use-package elixir-mode
:defer t))
(defun elixir/pre-init-popwin ()
(spacemacs|use-package-add-hook popwin
:post-config
(push '("*mix*" :tail t :noselect t) popwin:special-display-config)))